9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

Merge pull request #219 from Xiao-MoMi/dev

0.0.57
This commit is contained in:
XiaoMoMi
2025-06-13 04:08:08 +08:00
committed by GitHub
189 changed files with 4269 additions and 1699 deletions

View File

@@ -6,14 +6,11 @@
</h1> </h1>
<p align="center"> <p align="center">
<a href="https://github.com/Xiao-MoMi/craft-engine/">
<img src="https://sloc.xyz/github/Xiao-MoMi/craft-engine/?category=code" alt="Scc Count Badge"/>
</a>
<a href="https://deepwiki.com/Xiao-MoMi/craft-engine"> <a href="https://deepwiki.com/Xiao-MoMi/craft-engine">
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"> <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
</a> </a>
<a href="https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine" alt="GitBook"> <a href="https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine" alt="GitBook">
<img src="https://img.shields.io/badge/Docs-User Manual-D2691E" alt="Gitbook"/> <img src="https://img.shields.io/badge/📙-User Manual-D2691E" alt="Gitbook"/>
</a> </a>
</p> </p>
@@ -55,7 +52,7 @@ The code you contribute will be open-sourced under the GPLv3 license. If you pre
### 🌍 Translations ### 🌍 Translations
1. Clone this repository. 1. Clone this repository.
2. Create a new language file in: `/common-files/src/main/resources/translations` 2. Create a new language file in: `/common-files/src/main/resources/translations`
3. Once done, submit a **pull request** for review. We appreciate your contributions! 3. Once done, submit a **pull request** to **dev** branch for review. We appreciate your contributions!
## Differences Between Versions ## Differences Between Versions
| Version | Official Support | Max Players | Dev Builds | | Version | Official Support | Max Players | Dev Builds |
@@ -79,7 +76,7 @@ repositories {
``` ```
```kotlin ```kotlin
dependencies { dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.56") compileOnly("net.momirealms:craft-engine-core:0.0.57")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.56") compileOnly("net.momirealms:craft-engine-bukkit:0.0.57")
} }
``` ```

View File

@@ -27,7 +27,7 @@ subprojects {
expand(rootProject.properties) expand(rootProject.properties)
} }
filesMatching(arrayListOf("commands.yml", "config.yml", "*/*.yml", "ignite.mod.json")) { filesMatching(arrayListOf("commands.yml", "config.yml")) {
expand( expand(
Pair("project_version", rootProject.properties["project_version"]), Pair("project_version", rootProject.properties["project_version"]),
Pair("config_version", rootProject.properties["config_version"]), Pair("config_version", rootProject.properties["config_version"]),

View File

@@ -7,7 +7,7 @@ repositories {
maven("https://repo.rapture.pw/repository/maven-releases/") // slime world maven("https://repo.rapture.pw/repository/maven-releases/") // slime world
maven("https://repo.infernalsuite.com/repository/maven-snapshots/") // slime world maven("https://repo.infernalsuite.com/repository/maven-snapshots/") // slime world
maven("https://repo.momirealms.net/releases/") maven("https://repo.momirealms.net/releases/")
maven("https://mvn.lumine.io/repository/maven-public/") // model engine maven("https://mvn.lumine.io/repository/maven-public/") // model engine mythic mobs
maven("https://nexus.phoenixdevt.fr/repository/maven-public/") // mmoitems maven("https://nexus.phoenixdevt.fr/repository/maven-public/") // mmoitems
maven("https://repo.viaversion.com") // via maven("https://repo.viaversion.com") // via
maven("https://repo.skriptlang.org/releases/") // skript maven("https://repo.skriptlang.org/releases/") // skript
@@ -47,6 +47,8 @@ dependencies {
compileOnly(platform("com.intellectualsites.bom:bom-newest:1.52")) compileOnly(platform("com.intellectualsites.bom:bom-newest:1.52"))
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core") compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core")
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false } compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false }
// MythicMobs
compileOnly("io.lumine:Mythic-Dist:5.9.0")
} }
java { java {

View File

@@ -3,11 +3,13 @@ package net.momirealms.craftengine.bukkit.compatibility;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.compatibility.bettermodel.BetterModelModel; import net.momirealms.craftengine.bukkit.compatibility.bettermodel.BetterModelModel;
import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsProvider; import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsProvider;
import net.momirealms.craftengine.bukkit.compatibility.item.MythicMobsProvider;
import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider; import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider;
import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor; import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor;
import net.momirealms.craftengine.bukkit.compatibility.leveler.AuraSkillsLevelerProvider; import net.momirealms.craftengine.bukkit.compatibility.leveler.AuraSkillsLevelerProvider;
import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineModel; import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineModel;
import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineUtils; import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineUtils;
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicMobsListener;
import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils;
import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners; import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners;
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook; import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
@@ -104,6 +106,12 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
} }
if (this.isPluginEnabled("AuraSkills")) { if (this.isPluginEnabled("AuraSkills")) {
this.registerLevelerProvider("AuraSkills", new AuraSkillsLevelerProvider()); this.registerLevelerProvider("AuraSkills", new AuraSkillsLevelerProvider());
logHook("AuraSkills");
}
if (this.isPluginEnabled("MythicMobs")) {
BukkitItemManager.instance().registerExternalItemProvider(new MythicMobsProvider());
new MythicMobsListener(this.plugin);
logHook("MythicMobs");
} }
} }

View File

@@ -0,0 +1,25 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import io.lumine.mythic.bukkit.MythicBukkit;
import net.momirealms.craftengine.core.item.ExternalItemProvider;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class MythicMobsProvider implements ExternalItemProvider<ItemStack> {
private MythicBukkit mythicBukkit;
@Override
public String plugin() {
return "MythicMobs";
}
@Nullable
@Override
public ItemStack build(String id, ItemBuildContext context) {
if (mythicBukkit == null || mythicBukkit.isClosed()) {
this.mythicBukkit = MythicBukkit.inst();
}
return mythicBukkit.getItemManager().getItemStack(id);
}
}

View File

@@ -0,0 +1,64 @@
package net.momirealms.craftengine.bukkit.compatibility.mythicmobs;
import io.lumine.mythic.api.adapters.AbstractItemStack;
import io.lumine.mythic.api.adapters.AbstractPlayer;
import io.lumine.mythic.api.config.MythicLineConfig;
import io.lumine.mythic.api.drops.DropMetadata;
import io.lumine.mythic.api.drops.IItemDrop;
import io.lumine.mythic.api.skills.SkillCaster;
import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.bukkit.adapters.BukkitItemStack;
import io.lumine.mythic.core.drops.droppables.ItemDrop;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
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.ReflectionUtils;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.Constructor;
public class CraftEngineItemDrop extends ItemDrop implements IItemDrop {
private final CustomItem<ItemStack> customItem;
private static final Constructor<?> constructor$BukkitItemStack = ReflectionUtils.getConstructor(BukkitItemStack.class, ItemStack.class);
private static final boolean useReflection = constructor$BukkitItemStack != null;
public CraftEngineItemDrop(String line, MythicLineConfig config, CustomItem<ItemStack> customItem) {
super(line, config);
this.customItem = customItem;
CraftEngine.instance().debug(() -> "[MM调试] " + customItem.id() + " 注册成功");
}
@Override
public AbstractItemStack getDrop(DropMetadata dropMetadata, double amount) {
CraftEngine.instance().debug(() -> "[MM调试] getDrop() dropMetadata={" + dropMetadata + "}, amount={" + amount + "}");
ItemBuildContext context = ItemBuildContext.EMPTY;
SkillCaster caster = dropMetadata.getCaster();
if (caster != null && caster.getEntity() instanceof AbstractPlayer abstractPlayer) {
Entity bukkitEntity = abstractPlayer.getBukkitEntity();
if (bukkitEntity instanceof Player bukkitPlayer) {
var player = BukkitCraftEngine.instance().adapt(bukkitPlayer);
context = ItemBuildContext.of(player);
}
}
int amountInt = MCUtils.fastFloor(amount + 0.5F);
ItemStack itemStack = this.customItem.buildItemStack(context, amountInt);
return adapt(itemStack).amount(amountInt);
}
private static AbstractItemStack adapt(ItemStack itemStack) {
if (useReflection) {
try {
return (AbstractItemStack) constructor$BukkitItemStack.newInstance(itemStack);
} catch (Exception e) {
CraftEngine.instance().logger().warn("adapt(ItemStack itemStack) error: " + e.getMessage());
return null;
}
} else {
return BukkitAdapter.adapt(itemStack);
}
}
}

View File

@@ -0,0 +1,31 @@
package net.momirealms.craftengine.bukkit.compatibility.mythicmobs;
import io.lumine.mythic.api.config.MythicLineConfig;
import io.lumine.mythic.bukkit.events.MythicDropLoadEvent;
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.Listener;
public class MythicMobsListener implements Listener {
private final BukkitCraftEngine plugin;
public MythicMobsListener(BukkitCraftEngine plugin) {
this.plugin = plugin;
Bukkit.getPluginManager().registerEvents(this, plugin.javaPlugin());
}
@EventHandler
public void onMythicDropLoad(MythicDropLoadEvent event) {
if (!event.getDropName().equalsIgnoreCase("craftengine")) return;
String argument = event.getArgument();
plugin.debug(() -> "[MM调试] " + argument);
Key itemId = Key.of(argument);
this.plugin.itemManager().getCustomItem(itemId).ifPresent(customItem -> {
String line = event.getContainer().getConfigLine();
MythicLineConfig config = event.getConfig();
event.register(new CraftEngineItemDrop(line, config, customItem));
});
}
}

View File

@@ -59,6 +59,8 @@ public class ImageExpansion extends PlaceholderExpansion {
int codepoint; int codepoint;
if (param.length == 4) { if (param.length == 4) {
codepoint = image.codepointAt(Integer.parseInt(param[2]), Integer.parseInt(param[3])); codepoint = image.codepointAt(Integer.parseInt(param[2]), Integer.parseInt(param[3]));
} else if (param.length == 3) {
codepoint = image.codepointAt(Integer.parseInt(param[2]), 0);
} else if (param.length == 2) { } else if (param.length == 2) {
codepoint = image.codepointAt(0,0); codepoint = image.codepointAt(0,0);
} else { } else {

View File

@@ -7,14 +7,13 @@ import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.util.Kleenean; import ch.njol.util.Kleenean;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems; import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class ExprCustomItem extends SimpleExpression<ItemStack> { public class ExprCustomItem extends SimpleExpression<ItemStack> {
public static void register() { public static void register() {
@@ -36,7 +35,8 @@ public class ExprCustomItem extends SimpleExpression<ItemStack> {
String itemId = this.itemId.getSingle(e); String itemId = this.itemId.getSingle(e);
if (itemId == null) if (itemId == null)
return null; return null;
return new ItemStack[] {Objects.requireNonNull(CraftEngineItems.byId(Key.of(itemId))).buildItemStack(ItemBuildContext.EMPTY)}; CustomItem<ItemStack> customItem = CraftEngineItems.byId(Key.of(itemId));
return customItem == null ? null : new ItemStack[] {customItem.buildItemStack(ItemBuildContext.EMPTY)};
} }
@Override @Override

View File

@@ -5,7 +5,7 @@ import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Objects; import java.util.Optional;
public class ExprEntityFurnitureID extends SimplePropertyExpression<Object, String> { public class ExprEntityFurnitureID extends SimplePropertyExpression<Object, String> {
@@ -15,8 +15,11 @@ public class ExprEntityFurnitureID extends SimplePropertyExpression<Object, Stri
@Override @Override
public @Nullable String convert(Object object) { public @Nullable String convert(Object object) {
if (object instanceof Entity entity && CraftEngineFurniture.isFurniture(entity)) if (object instanceof Entity entity && CraftEngineFurniture.isFurniture(entity)) {
return Objects.requireNonNull(CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity)).id().toString(); return Optional.ofNullable(CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity))
.map(it -> it.id().toString())
.orElse(null);
}
return null; return null;
} }

View File

@@ -71,27 +71,47 @@ paper {
required = false required = false
load = PaperPluginDescription.RelativeLoadOrder.BEFORE load = PaperPluginDescription.RelativeLoadOrder.BEFORE
} }
register("NeigeItems") { register("LuckPerms") { required = false }
required = false register("ViaVersion") { required = false }
}
register("MMOItems") { // external models
required = false register("ModelEngine") { required = false }
} register("BetterModel") { required = false }
register("ModelEngine") {
required = false // external items
} register("NeigeItems") { required = false }
register("BetterModel") { register("MMOItems") { required = false }
required = false register("MythicMobs") { required = false }
}
register("AuraSkills") { // leveler
required = false register("AuraSkills") { required = false }
}
register("LuckPerms") { // anti grief lib
required = false register("Dominion") { required = false }
} register("WorldGuard") { required = false }
register("ViaVersion") { register("Kingdoms") { required = false }
required = false register("Lands") { required = false }
} register("IridiumSkyblock") { required = false }
register("CrashClaim") { required = false }
register("GriefDefender") { required = false }
register("HuskClaims") { required = false }
register("BentoBox") { required = false }
register("HuskTowns") { required = false }
register("PlotSquared") { required = false }
register("Residence") { required = false }
register("SuperiorSkyblock2") { required = false }
register("Towny") { required = false }
register("FabledSkyBlock") { required = false }
register("GriefPrevention") { required = false }
register("RedProtect") { required = false }
register("Landlord") { required = false }
register("uSkyBlock") { required = false }
register("XClaim") { required = false }
register("UltimateClaims") { required = false }
register("UltimateClans") { required = false }
register("PreciousStones") { required = false }
register("hClaims") { required = false }
register("Factions") { required = false }
} }
} }

View File

@@ -50,6 +50,7 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap {
new ModernEventHandler(context, this.plugin).register(); new ModernEventHandler(context, this.plugin).register();
} else { } else {
try { try {
logger.info("Patching the server...");
RuntimePatcher.patch(this.plugin); RuntimePatcher.patch(this.plugin);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to patch server", e); throw new RuntimeException("Failed to patch server", e);

View File

@@ -52,7 +52,7 @@ public final class CraftEngineFurniture {
public static BukkitFurniture place(Location location, Key furnitureId) { public static BukkitFurniture place(Location location, Key furnitureId) {
CustomFurniture furniture = byId(furnitureId); CustomFurniture furniture = byId(furnitureId);
if (furniture == null) return null; if (furniture == null) return null;
return place(location, furnitureId, furniture.getAnyPlacement()); return place(location, furnitureId, furniture.getAnyAnchorType());
} }
/** /**

View File

@@ -28,7 +28,6 @@ import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@@ -371,19 +370,18 @@ public class BlockEventListener implements Listener {
// for vanilla blocks // for vanilla blocks
if (event.getChangedType() == Material.NOTE_BLOCK) { if (event.getChangedType() == Material.NOTE_BLOCK) {
Block block = event.getBlock(); Block block = event.getBlock();
Block sourceBlock = event.getSourceBlock();
if (block.getX() == sourceBlock.getX() && block.getX() == sourceBlock.getZ()) {
World world = block.getWorld(); World world = block.getWorld();
Location location = block.getLocation(); Location location = block.getLocation();
Block sourceBlock = event.getSourceBlock();
BlockFace direction = sourceBlock.getFace(block);
if (direction == BlockFace.UP || direction == BlockFace.DOWN) {
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world); Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world);
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
Object blockPos = LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); Object blockPos = LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos); FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
if (direction == BlockFace.UP) { if (block.getY() > sourceBlock.getY()) {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, 0); NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, Config.maxNoteBlockChainUpdate());
} else { } else {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$DOWN, blockPos, 0); NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$DOWN, blockPos, Config.maxNoteBlockChainUpdate());
} }
} }
} }

View File

@@ -19,6 +19,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; 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.MRegistries;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.RegistryUtils; import net.momirealms.craftengine.bukkit.util.RegistryUtils;
@@ -31,7 +32,6 @@ import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.ResourceLocation;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
@@ -45,7 +45,6 @@ import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.Registry; import org.bukkit.Registry;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -93,11 +92,9 @@ public class BukkitBlockManager extends AbstractBlockManager {
this.blockParser = new BlockParser(); this.blockParser = new BlockParser();
this.initVanillaRegistry(); this.initVanillaRegistry();
this.loadMappingsAndAdditionalBlocks(); this.loadMappingsAndAdditionalBlocks();
if (!plugin.requiresRestart()) {
this.registerBlocks(); this.registerBlocks();
this.registerEmptyBlock(); this.registerEmptyBlock();
} }
}
@Override @Override
public void init() { public void init() {
@@ -163,9 +160,9 @@ public class BukkitBlockManager extends AbstractBlockManager {
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) { for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue())); list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
} }
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.instance$Registries$BLOCK, list)); Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, list));
for (Player player : Bukkit.getOnlinePlayers()) { for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
this.plugin.networkManager().sendPacket(this.plugin.adapt(player), packet); player.sendPacket(packet, false);
} }
// 如果空,那么新来的玩家就没必要收到更新包了 // 如果空,那么新来的玩家就没必要收到更新包了
if (list.isEmpty()) { if (list.isEmpty()) {
@@ -276,19 +273,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
} }
private void initVanillaRegistry() { private void initVanillaRegistry() {
int vanillaStateCount; int vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
if (this.plugin.hasMod()) {
try {
Class<?> modClass = ReflectionUtils.getClazz(CraftEngine.MOD_CLASS);
Field amountField = ReflectionUtils.getDeclaredField(modClass, "vanillaRegistrySize");
vanillaStateCount = amountField.getInt(null);
} catch (Exception e) {
vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
this.plugin.logger().severe("Fatal error", e);
}
} else {
vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
}
this.plugin.logger().info("Vanilla block count: " + vanillaStateCount); this.plugin.logger().info("Vanilla block count: " + vanillaStateCount);
BlockStateUtils.init(vanillaStateCount); BlockStateUtils.init(vanillaStateCount);
} }
@@ -512,10 +497,13 @@ public class BukkitBlockManager extends AbstractBlockManager {
throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath); throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath);
} }
json.addProperty("model", modelPath); json.addProperty("model", modelPath);
if (singleModelMap.containsKey("x")) json.addProperty("x", ResourceConfigUtils.getAsInt(singleModelMap.get("x"), "x")); if (singleModelMap.containsKey("x"))
if (singleModelMap.containsKey("y")) json.addProperty("y", ResourceConfigUtils.getAsInt(singleModelMap.get("y"), "y")); json.addProperty("x", ResourceConfigUtils.getAsInt(singleModelMap.get("x"), "x"));
if (singleModelMap.containsKey("y"))
json.addProperty("y", ResourceConfigUtils.getAsInt(singleModelMap.get("y"), "y"));
if (singleModelMap.containsKey("uvlock")) json.addProperty("uvlock", (boolean) singleModelMap.get("uvlock")); if (singleModelMap.containsKey("uvlock")) json.addProperty("uvlock", (boolean) singleModelMap.get("uvlock"));
if (singleModelMap.containsKey("weight")) json.addProperty("weight", ResourceConfigUtils.getAsInt(singleModelMap.get("weight"), "weight")); if (singleModelMap.containsKey("weight"))
json.addProperty("weight", ResourceConfigUtils.getAsInt(singleModelMap.get("weight"), "weight"));
Map<String, Object> generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true); Map<String, Object> generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true);
if (generationMap != null) { if (generationMap != null) {
prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap)); prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap));
@@ -591,6 +579,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
Object resourceLocation = KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK); Object resourceLocation = KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK);
Object block = CoreReflections.method$Registry$get.invoke(MBuiltInRegistries.BLOCK, resourceLocation); Object block = CoreReflections.method$Registry$get.invoke(MBuiltInRegistries.BLOCK, resourceLocation);
Object stateDefinition = CoreReflections.field$Block$StateDefinition.get(block); Object stateDefinition = CoreReflections.field$Block$StateDefinition.get(block);
@SuppressWarnings("unchecked")
ImmutableList<Object> states = (ImmutableList<Object>) CoreReflections.field$StateDefinition$states.get(stateDefinition); ImmutableList<Object> states = (ImmutableList<Object>) CoreReflections.field$StateDefinition$states.get(stateDefinition);
for (Object state : states) { for (Object state : states) {
BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(state, new Object()); BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(state, new Object());
@@ -723,14 +712,6 @@ public class BukkitBlockManager extends AbstractBlockManager {
Object blockHolder; Object blockHolder;
Object resourceLocation = createResourceLocation(realBlockKey); Object resourceLocation = createResourceLocation(realBlockKey);
if (this.plugin.hasMod()) {
newRealBlock = CoreReflections.method$Registry$get.invoke(MBuiltInRegistries.BLOCK, resourceLocation);
newBlockState = getOnlyBlockState(newRealBlock);
@SuppressWarnings("unchecked")
Optional<Object> optionalHolder = (Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(MBuiltInRegistries.BLOCK, CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, resourceLocation));
blockHolder = optionalHolder.get();
} else {
try { try {
newRealBlock = BlockGenerator.generateBlock(clientSideBlockType, clientSideBlock, blockProperties); newRealBlock = BlockGenerator.generateBlock(clientSideBlockType, clientSideBlock, blockProperties);
} catch (Throwable throwable) { } catch (Throwable throwable) {
@@ -744,7 +725,6 @@ public class BukkitBlockManager extends AbstractBlockManager {
newBlockState = getOnlyBlockState(newRealBlock); newBlockState = getOnlyBlockState(newRealBlock);
CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState); CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState);
}
if (isNoteBlock) { if (isNoteBlock) {
BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(newBlockState, new Object()); BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(newBlockState, new Object());
@@ -782,7 +762,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
private Object createBlockProperties(Key realBlockKey) throws Exception { private Object createBlockProperties(Key realBlockKey) throws Exception {
Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null); Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null);
Object realBlockResourceLocation = createResourceLocation(realBlockKey); Object realBlockResourceLocation = createResourceLocation(realBlockKey);
Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, realBlockResourceLocation); Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.BLOCK, realBlockResourceLocation);
if (CoreReflections.field$BlockBehaviour$Properties$id != null) { if (CoreReflections.field$BlockBehaviour$Properties$id != null) {
CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey); CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey);
} }

View File

@@ -129,13 +129,12 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
// init cache // init cache
CoreReflections.method$BlockStateBase$initCache.invoke(mcBlockState); CoreReflections.method$BlockStateBase$initCache.invoke(mcBlockState);
// set block light // set block light
if (settings.blockLight() != -1) { int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(state.vanillaBlockState().handle());
if (VersionHelper.isOrAbove1_21_2()) { if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.field$BlockStateBase$lightBlock.set(mcBlockState, settings.blockLight()); CoreReflections.field$BlockStateBase$lightBlock.set(mcBlockState, blockLight);
} else { } else {
Object cache = CoreReflections.field$BlockStateBase$cache.get(mcBlockState); Object cache = CoreReflections.field$BlockStateBase$cache.get(mcBlockState);
CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, settings.blockLight()); CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight);
}
} }
// set fluid later // set fluid later
if (settings.fluidState()) { if (settings.fluidState()) {
@@ -151,7 +150,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId()); Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId());
Set<Object> tags = new HashSet<>(); Set<Object> tags = new HashSet<>();
for (Key tag : settings.tags()) { for (Key tag : settings.tags()) {
tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, KeyUtils.toResourceLocation(tag))); tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(tag)));
} }
CoreReflections.field$Holder$Reference$tags.set(holder, tags); CoreReflections.field$Holder$Reference$tags.set(holder, tags);
// set burning properties // set burning properties

View File

@@ -65,7 +65,7 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception { public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object world = args[1]; Object world = args[1];
Object blockPos = args[2]; Object blockPos = args[2];
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2); FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
} }
@Override @Override
@@ -86,7 +86,7 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio
return state; return state;
} }
if (this.delay != 0) { if (this.delay != 0) {
CoreReflections.method$LevelAccessor$scheduleTick.invoke(level, blockPos, thisBlock, this.delay); FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(level, blockPos, thisBlock, this.delay);
return state; return state;
} }
if (!canSurvive(thisBlock, new Object[] {state, level, blockPos}, () -> true)) { if (!canSurvive(thisBlock, new Object[] {state, level, blockPos}, () -> true)) {

View File

@@ -36,7 +36,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception { public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object world = args[1]; Object world = args[1];
Object blockPos = args[2]; Object blockPos = args[2];
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2); FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
} }
@Override @Override
@@ -50,7 +50,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
world = args[3]; world = args[3];
blockPos = args[4]; blockPos = args[4];
} }
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2); FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
return args[0]; return args[0];
} }

View File

@@ -62,7 +62,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
boolean lit = state.get(this.litProperty); boolean lit = state.get(this.litProperty);
if (lit != FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) { if (lit != FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
if (lit) { if (lit) {
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 4); FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 4);
} else { } else {
// TODO Call Event // TODO Call Event
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2); FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2);

View File

@@ -85,7 +85,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
LeavesBlockBehavior behavior = optionalBehavior.get(); LeavesBlockBehavior behavior = optionalBehavior.get();
int distance = behavior.getDistanceAt(neighborState) + 1; int distance = behavior.getDistanceAt(neighborState) + 1;
if (distance != 1 || behavior.getDistance(thisState) != distance) { if (distance != 1 || behavior.getDistance(thisState) != distance) {
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 1); FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 1);
} }
} }
} }

View File

@@ -77,7 +77,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
} }
private void generateTree(Object world, Object blockPos, Object blockState, Object randomSource) throws Exception { private void generateTree(Object world, Object blockPos, Object blockState, Object randomSource) throws Exception {
Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$CONFIGURED_FEATURE); Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.CONFIGURED_FEATURE);
if (registry == null) return; if (registry == null) return;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Optional<Object> holder = (Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(registry, FeatureUtils.createFeatureKey(treeFeature())); Optional<Object> holder = (Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(registry, FeatureUtils.createFeatureKey(treeFeature()));

View File

@@ -7,20 +7,21 @@ public interface EntityData<T> {
Object serializer(); Object serializer();
int id(); int id();
T defaultValue(); T defaultValue();
Object entityDataAccessor();
default Object createEntityDataIfNotDefaultValue(T value) { default Object createEntityDataIfNotDefaultValue(T value) {
if (defaultValue().equals(value)) return null; if (defaultValue().equals(value)) return null;
return EntityDataValue.create(id(), serializer(), value); return EntityDataValue.create(id(), serializer(), entityDataAccessor(), value);
} }
default void addEntityDataIfNotDefaultValue(T value, List<Object> list) { default void addEntityDataIfNotDefaultValue(T value, List<Object> list) {
if (!defaultValue().equals(value)) { if (!defaultValue().equals(value)) {
list.add(EntityDataValue.create(id(), serializer(), value)); list.add(EntityDataValue.create(id(), serializer(), entityDataAccessor(), value));
} }
} }
default void addEntityData(T value, List<Object> list) { default void addEntityData(T value, List<Object> list) {
list.add(EntityDataValue.create(id(), serializer(), value)); list.add(EntityDataValue.create(id(), serializer(), entityDataAccessor(), value));
} }
static <T> EntityData<T> of(int id, Object serializer, T defaultValue) { static <T> EntityData<T> of(int id, Object serializer, T defaultValue) {

View File

@@ -98,8 +98,7 @@ public class EntityDataValue {
throw new IllegalAccessError("Utility class"); throw new IllegalAccessError("Utility class");
} }
public static Object create(int id, Object serializer, Object value) { public static Object create(int id, Object serializer, Object entityDataAccessor, Object value) {
Object entityDataAccessor = FastNMS.INSTANCE.constructor$EntityDataAccessor(id, serializer);
return FastNMS.INSTANCE.method$SynchedEntityData$DataValue$create(entityDataAccessor, value); return FastNMS.INSTANCE.method$SynchedEntityData$DataValue$create(entityDataAccessor, value);
} }
} }

View File

@@ -1,14 +1,18 @@
package net.momirealms.craftengine.bukkit.entity.data; package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
public class SimpleEntityData<T> implements EntityData<T> { public class SimpleEntityData<T> implements EntityData<T> {
private final int id; private final int id;
private final Object serializer; private final Object serializer;
private final T defaultValue; private final T defaultValue;
private final Object entityDataAccessor;
public SimpleEntityData(int id, Object serializer, T defaultValue) { public SimpleEntityData(int id, Object serializer, T defaultValue) {
this.id = id; this.id = id;
this.serializer = serializer; this.serializer = serializer;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
this.entityDataAccessor = FastNMS.INSTANCE.constructor$EntityDataAccessor(id, serializer);
} }
@Override @Override
@@ -25,4 +29,9 @@ public class SimpleEntityData<T> implements EntityData<T> {
public T defaultValue() { public T defaultValue() {
return defaultValue; return defaultValue;
} }
@Override
public Object entityDataAccessor() {
return entityDataAccessor;
}
} }

View File

@@ -32,7 +32,7 @@ import java.util.*;
public class BukkitFurniture implements Furniture { public class BukkitFurniture implements Furniture {
private final Key id; private final Key id;
private final CustomFurniture furniture; private final CustomFurniture furniture;
private final AnchorType anchorType; private final CustomFurniture.Placement placement;
private FurnitureExtraData extraData; private FurnitureExtraData extraData;
// location // location
private final Location location; private final Location location;
@@ -61,7 +61,7 @@ public class BukkitFurniture implements Furniture {
this.id = furniture.id(); this.id = furniture.id();
this.extraData = extraData; this.extraData = extraData;
this.baseEntityId = baseEntity.getEntityId(); this.baseEntityId = baseEntity.getEntityId();
this.anchorType = extraData.anchorType().orElse(furniture.getAnyPlacement());
this.location = baseEntity.getLocation(); this.location = baseEntity.getLocation();
this.baseEntity = new WeakReference<>(baseEntity); this.baseEntity = new WeakReference<>(baseEntity);
this.furniture = furniture; this.furniture = furniture;
@@ -70,7 +70,7 @@ public class BukkitFurniture implements Furniture {
List<Integer> mainEntityIds = new IntArrayList(); List<Integer> mainEntityIds = new IntArrayList();
mainEntityIds.add(this.baseEntityId); mainEntityIds.add(this.baseEntityId);
CustomFurniture.Placement placement = furniture.getPlacement(anchorType); this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType));
// bind external furniture // bind external furniture
Optional<ExternalModel> optionalExternal = placement.externalModel(); Optional<ExternalModel> optionalExternal = placement.externalModel();
if (optionalExternal.isPresent()) { if (optionalExternal.isPresent()) {
@@ -171,7 +171,7 @@ public class BukkitFurniture implements Furniture {
@NotNull @NotNull
public Location dropLocation() { public Location dropLocation() {
Optional<Vector3f> dropOffset = config().getPlacement(this.anchorType).dropOffset(); Optional<Vector3f> dropOffset = this.placement.dropOffset();
if (dropOffset.isEmpty()) { if (dropOffset.isEmpty()) {
return location(); return location();
} }
@@ -275,7 +275,7 @@ public class BukkitFurniture implements Furniture {
@Override @Override
public @NotNull AnchorType anchorType() { public @NotNull AnchorType anchorType() {
return this.anchorType; return this.placement.anchorType();
} }
@Override @Override

View File

@@ -49,7 +49,7 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
Vector3f offset = conjugated.transform(new Vector3f(position())); Vector3f offset = conjugated.transform(new Vector3f(position()));
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(), entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(),
MEntityTypes.instance$EntityType$ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
)); ));
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(dyedColor))); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(dyedColor)));
} }

View File

@@ -40,7 +40,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY); public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY);
public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION); public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION);
public static Class<?> COLLISION_ENTITY_CLASS = Interaction.class; public static Class<?> COLLISION_ENTITY_CLASS = Interaction.class;
public static Object NMS_COLLISION_ENTITY_TYPE = MEntityTypes.instance$EntityType$INTERACTION; public static Object NMS_COLLISION_ENTITY_TYPE = MEntityTypes.INTERACTION;
public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION; public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION;
private static BukkitFurnitureManager instance; private static BukkitFurnitureManager instance;
private final BukkitCraftEngine plugin; private final BukkitCraftEngine plugin;
@@ -70,7 +70,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) { public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
Optional<AnchorType> optionalAnchorType = extraData.anchorType(); Optional<AnchorType> optionalAnchorType = extraData.anchorType();
if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) {
extraData.anchorType(furniture.getAnyPlacement()); extraData.anchorType(furniture.getAnyAnchorType());
} }
Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> {
ItemDisplay display = (ItemDisplay) entity; ItemDisplay display = (ItemDisplay) entity;
@@ -92,7 +92,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
@Override @Override
public void delayedInit() { public void delayedInit() {
COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class; COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class;
NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.instance$EntityType$INTERACTION : MEntityTypes.instance$EntityType$OAK_BOAT; NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT;
COLLISION_ENTITY_TYPE = Config.colliderType(); COLLISION_ENTITY_TYPE = Config.colliderType();
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin()); Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin());
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin()); Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin());

View File

@@ -62,7 +62,7 @@ public class InteractionHitBox extends AbstractHitBox {
float yaw = position.xRot(); float yaw = position.xRot();
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
MEntityTypes.instance$EntityType$INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
if (canUseItemOn()) { if (canUseItemOn()) {

View File

@@ -61,7 +61,7 @@ public class ShulkerHitBox extends AbstractHitBox {
if (interactionEntity) { if (interactionEntity) {
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
MEntityTypes.instance$EntityType$INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) { if (canUseOn) {
@@ -80,7 +80,7 @@ public class ShulkerHitBox extends AbstractHitBox {
if (interactionEntity) { if (interactionEntity) {
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw, entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw,
MEntityTypes.instance$EntityType$INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) { if (canUseOn) {
@@ -102,14 +102,14 @@ public class ShulkerHitBox extends AbstractHitBox {
// first interaction // first interaction
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
MEntityTypes.instance$EntityType$INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
// second interaction // second interaction
double distance = shulkerHeight - scale; double distance = shulkerHeight - scale;
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw, entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw,
MEntityTypes.instance$EntityType$INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) { if (canUseOn) {
@@ -213,11 +213,11 @@ public class ShulkerHitBox extends AbstractHitBox {
double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY; double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY;
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw, entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw,
MEntityTypes.instance$EntityType$ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
), false); ), false);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw,
MEntityTypes.instance$EntityType$SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0
), false); ), false);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(this.cachedShulkerValues)), false); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(this.cachedShulkerValues)), false);
// add passengers // add passengers

View File

@@ -11,4 +11,9 @@ public class BukkitCustomProjectile extends AbstractCustomProjectile {
public BukkitCustomProjectile(ProjectileMeta meta, Projectile projectile, Item<ItemStack> projectileItem) { public BukkitCustomProjectile(ProjectileMeta meta, Projectile projectile, Item<ItemStack> projectileItem) {
super(meta, new BukkitProjectile(projectile), projectileItem); super(meta, new BukkitProjectile(projectile), projectileItem);
} }
@Override
public BukkitProjectile projectile() {
return (BukkitProjectile) super.projectile();
}
} }

View File

@@ -38,11 +38,11 @@ import java.util.concurrent.ConcurrentHashMap;
public class BukkitProjectileManager implements Listener, ProjectileManager { public class BukkitProjectileManager implements Listener, ProjectileManager {
private static BukkitProjectileManager instance; private static BukkitProjectileManager instance;
private final BukkitCraftEngine plugin; private final BukkitCraftEngine plugin;
private final Map<Integer, BukkitCustomProjectile> projectiles; // 会被netty线程访问
private final Map<Integer, BukkitCustomProjectile> projectiles = new ConcurrentHashMap<>();
public BukkitProjectileManager(BukkitCraftEngine plugin) { public BukkitProjectileManager(BukkitCraftEngine plugin) {
this.plugin = plugin; this.plugin = plugin;
this.projectiles = new ConcurrentHashMap<>();
instance = this; instance = this;
} }
@@ -114,7 +114,8 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
wrapped.getCustomItem().ifPresent(it -> { wrapped.getCustomItem().ifPresent(it -> {
ProjectileMeta meta = it.settings().projectileMeta(); ProjectileMeta meta = it.settings().projectileMeta();
if (meta != null) { if (meta != null) {
this.projectiles.put(projectile.getEntityId(), new BukkitCustomProjectile(meta, projectile, wrapped)); BukkitCustomProjectile customProjectile = new BukkitCustomProjectile(meta, projectile, wrapped);
this.projectiles.put(projectile.getEntityId(), customProjectile);
new ProjectileInjectTask(projectile); new ProjectileInjectTask(projectile);
} }
}); });
@@ -161,7 +162,8 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
public class ProjectileInjectTask implements Runnable { public class ProjectileInjectTask implements Runnable {
private final Projectile projectile; private final Projectile projectile;
private final SchedulerTask task; private final SchedulerTask task;
private boolean injected; private Object cachedServerEntity;
private int lastInjectedInterval = 0;
public ProjectileInjectTask(Projectile projectile) { public ProjectileInjectTask(Projectile projectile) {
this.projectile = projectile; this.projectile = projectile;
@@ -178,32 +180,42 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
this.task.cancel(); this.task.cancel();
return; return;
} }
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(this.projectile); Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(this.projectile);
if (!this.injected) { // 获取server entity
if (this.cachedServerEntity == null) {
Object trackedEntity = FastNMS.INSTANCE.field$Entity$trackedEntity(nmsEntity); Object trackedEntity = FastNMS.INSTANCE.field$Entity$trackedEntity(nmsEntity);
if (trackedEntity == null) { if (trackedEntity == null) return;
return; Object serverEntity = FastNMS.INSTANCE.field$ChunkMap$TrackedEntity$serverEntity(trackedEntity);
if (serverEntity == null) return;
this.cachedServerEntity = serverEntity;
} }
Object serverEntity = FastNMS.INSTANCE.filed$ChunkMap$TrackedEntity$serverEntity(trackedEntity);
if (serverEntity == null) { boolean inGround = FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity);
return; if (canSpawnParticle(nmsEntity, inGround)) {
}
try {
CoreReflections.field$ServerEntity$updateInterval.set(serverEntity, 1);
this.injected = true;
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to update server entity tracking interval", e);
}
}
if (canSpawnParticle(nmsEntity)) {
this.projectile.getWorld().spawnParticle(ParticleUtils.BUBBLE, this.projectile.getLocation(), 3, 0.1, 0.1, 0.1, 0); this.projectile.getWorld().spawnParticle(ParticleUtils.BUBBLE, this.projectile.getLocation(), 3, 0.1, 0.1, 0.1, 0);
} }
if (inGround) {
updateProjectileUpdateInterval(Integer.MAX_VALUE);
} else {
updateProjectileUpdateInterval(1);
}
} }
private static boolean canSpawnParticle(Object nmsEntity) { private void updateProjectileUpdateInterval(int updateInterval) {
if (this.lastInjectedInterval == updateInterval) return;
try {
CoreReflections.methodHandle$ServerEntity$updateIntervalSetter.invokeExact(this.cachedServerEntity, updateInterval);
this.lastInjectedInterval = updateInterval;
} catch (Throwable e) {
BukkitProjectileManager.this.plugin.logger().warn("Failed to update server entity update interval for " + this.projectile.getType().getKey() + "[" + this.projectile.getUniqueId() + "]", e);
}
}
private static boolean canSpawnParticle(Object nmsEntity, boolean inGround) {
if (!FastNMS.INSTANCE.field$Entity$wasTouchingWater(nmsEntity)) return false; if (!FastNMS.INSTANCE.field$Entity$wasTouchingWater(nmsEntity)) return false;
if (CoreReflections.clazz$AbstractArrow.isInstance(nmsEntity)) { if (CoreReflections.clazz$AbstractArrow.isInstance(nmsEntity)) {
return !FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity); return !inGround;
} }
return true; return true;
} }

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.bukkit.item; package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
@@ -10,7 +10,6 @@ import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.ArrayList; import java.util.ArrayList;
@@ -19,22 +18,23 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class BukkitCustomItem extends AbstractCustomItem<ItemStack> { public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
private final Material material; private final Object item;
private final Object clientItem;
public BukkitCustomItem(Holder<Key> id, Key materialKey, Material material, public BukkitCustomItem(Holder<Key> id, Object item, Object clientItem, Key materialKey, Key clientBoundMaterialKey,
List<ItemBehavior> behaviors, List<ItemBehavior> behaviors,
List<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers, List<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers,
ItemSettings settings, ItemSettings settings,
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) { Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
super(id, materialKey, behaviors, modifiers, clientBoundModifiers, settings, events); super(id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events);
this.material = material; this.item = item;
this.clientItem = clientItem;
} }
@Override @Override
public ItemStack buildItemStack(ItemBuildContext context, int count) { public ItemStack buildItemStack(ItemBuildContext context, int count) {
ItemStack item = new ItemStack(this.material); ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.constructor$ItemStack(this.item, count));
Item<ItemStack> wrapped = BukkitCraftEngine.instance().itemManager().wrap(item); Item<ItemStack> wrapped = BukkitCraftEngine.instance().itemManager().wrap(item);
wrapped.count(count);
for (ItemDataModifier<ItemStack> modifier : this.modifiers) { for (ItemDataModifier<ItemStack> modifier : this.modifiers) {
modifier.apply(wrapped, context); modifier.apply(wrapped, context);
} }
@@ -43,7 +43,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
@Override @Override
public Item<ItemStack> buildItem(ItemBuildContext context) { public Item<ItemStack> buildItem(ItemBuildContext context) {
ItemStack item = new ItemStack(this.material); ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.constructor$ItemStack(this.item, 1));
Item<ItemStack> wrapped = BukkitCraftEngine.instance().itemManager().wrap(item); Item<ItemStack> wrapped = BukkitCraftEngine.instance().itemManager().wrap(item);
for (ItemDataModifier<ItemStack> modifier : dataModifiers()) { for (ItemDataModifier<ItemStack> modifier : dataModifiers()) {
modifier.apply(wrapped, context); modifier.apply(wrapped, context);
@@ -51,23 +51,33 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
return BukkitCraftEngine.instance().itemManager().wrap(wrapped.load()); return BukkitCraftEngine.instance().itemManager().wrap(wrapped.load());
} }
public static Builder<ItemStack> builder(Material material) { public Object clientItem() {
return new BuilderImpl(material); return clientItem;
}
public Object item() {
return item;
}
public static Builder<ItemStack> builder(Object item, Object clientBoundItem) {
return new BuilderImpl(item, clientBoundItem);
} }
public static class BuilderImpl implements Builder<ItemStack> { public static class BuilderImpl implements Builder<ItemStack> {
private Holder<Key> id; private Holder<Key> id;
private Key materialKey; private Key itemKey;
private final Material material; private final Object item;
private Key clientBoundItemKey;
private final Object clientBoundItem;
private final Map<EventTrigger, List<Function<PlayerOptionalContext>>> events = new EnumMap<>(EventTrigger.class); private final Map<EventTrigger, List<Function<PlayerOptionalContext>>> events = new EnumMap<>(EventTrigger.class);
private final List<ItemBehavior> behaviors = new ArrayList<>(4); private final List<ItemBehavior> behaviors = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4); private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4); private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4);
private ItemSettings settings; private ItemSettings settings;
public BuilderImpl(Material material) { public BuilderImpl(Object item, Object clientBoundItem) {
this.material = material; this.item = item;
this.materialKey = KeyUtils.namespacedKey2Key(material.getKey()); this.clientBoundItem = clientBoundItem;
} }
@Override @Override
@@ -76,9 +86,15 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
return this; return this;
} }
@Override
public Builder<ItemStack> clientBoundMaterial(Key clientBoundMaterial) {
this.clientBoundItemKey = clientBoundMaterial;
return this;
}
@Override @Override
public Builder<ItemStack> material(Key material) { public Builder<ItemStack> material(Key material) {
this.materialKey = material; this.itemKey = material;
return this; return this;
} }
@@ -133,7 +149,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
@Override @Override
public CustomItem<ItemStack> build() { public CustomItem<ItemStack> build() {
this.modifiers.addAll(this.settings.modifiers()); this.modifiers.addAll(this.settings.modifiers());
return new BukkitCustomItem(this.id, this.materialKey, this.material, List.copyOf(this.behaviors), return new BukkitCustomItem(this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors),
List.copyOf(this.modifiers), List.copyOf(this.clientBoundModifiers), this.settings, this.events); List.copyOf(this.modifiers), List.copyOf(this.clientBoundModifiers), this.settings, this.events);
} }
} }

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item;
import com.saicone.rtag.item.ItemTagStream; import com.saicone.rtag.item.ItemTagStream;
import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.FlintAndSteelItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory; import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory;
import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener; import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener;
@@ -12,6 +13,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; 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.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.ItemUtils; import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
@@ -43,6 +45,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
static { static {
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS); registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET); registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
registerVanillaItemExtraBehavior(FlintAndSteelItemBehavior.INSTANCE, ItemKeys.FLINT_AND_STEEL);
} }
private static BukkitItemManager instance; private static BukkitItemManager instance;
@@ -198,9 +201,19 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
} }
@Override @Override
protected CustomItem.Builder<ItemStack> createPlatformItemBuilder(Holder<Key> id, Key materialId) { protected CustomItem.Builder<ItemStack> createPlatformItemBuilder(Holder<Key> id, Key materialId, Key clientBoundMaterialId) {
Material material = ResourceConfigUtils.requireNonNullOrThrow(Registry.MATERIAL.get(KeyUtils.toNamespacedKey(materialId)), () -> new LocalizedResourceConfigException("warning.config.item.invalid_material", materialId.toString())); Object item = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(materialId));
return BukkitCustomItem.builder(material).material(materialId).id(id); Object clientBoundItem = materialId == clientBoundMaterialId ? item : FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(clientBoundMaterialId));
if (item == null) {
throw new LocalizedResourceConfigException("warning.config.item.invalid_material", materialId.toString());
}
if (clientBoundItem == null) {
throw new LocalizedResourceConfigException("warning.config.item.invalid_material", clientBoundMaterialId.toString());
}
return BukkitCustomItem.builder(item, clientBoundItem)
.id(id)
.material(materialId)
.clientBoundMaterial(clientBoundMaterialId);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -214,7 +227,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID) .orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id)); .register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
Object resourceLocation = KeyUtils.toResourceLocation(id.namespace(), id.value()); Object resourceLocation = KeyUtils.toResourceLocation(id.namespace(), id.value());
Object mcHolder = ((Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(MBuiltInRegistries.ITEM, CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$ITEM, resourceLocation))).get(); Object mcHolder = ((Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(MBuiltInRegistries.ITEM, CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.ITEM, resourceLocation))).get();
Set<Object> tags = (Set<Object>) CoreReflections.field$Holder$Reference$tags.get(mcHolder); Set<Object> tags = (Set<Object>) CoreReflections.field$Holder$Reference$tags.get(mcHolder);
for (Object tag : tags) { for (Object tag : tags) {
Key tagId = Key.of(CoreReflections.field$TagKey$location.get(tag).toString()); Key tagId = Key.of(CoreReflections.field$TagKey$location.get(tag).toString());

View File

@@ -111,6 +111,9 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
private void setComponentInternal(Object type, DynamicOps ops, Object value) { private void setComponentInternal(Object type, DynamicOps ops, Object value) {
if (value == null) return; if (value == null) return;
Object componentType = ensureDataComponentType(type); Object componentType = ensureDataComponentType(type);
if (componentType == null) {
return;
}
Codec codec = FastNMS.INSTANCE.method$DataComponentType$codec(componentType); Codec codec = FastNMS.INSTANCE.method$DataComponentType$codec(componentType);
try { try {
DataResult<Object> result = codec.parse(ops, value); DataResult<Object> result = codec.parse(ops, value);
@@ -162,4 +165,9 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
public void count(int amount) { public void count(int amount) {
this.item.setAmount(Math.max(amount, 0)); this.item.setAmount(Math.max(amount, 0));
} }
@Override
public void shrink(int amount) {
count(count() - amount);
}
} }

View File

@@ -8,18 +8,9 @@ import org.bukkit.inventory.ItemStack;
public class LegacyItemWrapper implements ItemWrapper<ItemStack> { public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
private final RtagItem rtagItem; private final RtagItem rtagItem;
private int count;
public LegacyItemWrapper(RtagItem rtagItem, int count) { public LegacyItemWrapper(RtagItem rtagItem) {
this.rtagItem = rtagItem; this.rtagItem = rtagItem;
this.count = count;
}
@Override
public ItemStack getItem() {
ItemStack itemStack = this.rtagItem.getItem();
itemStack.setAmount(this.count);
return itemStack;
} }
public boolean setTag(Object value, Object... path) { public boolean setTag(Object value, Object... path) {
@@ -49,12 +40,12 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
} }
public int count() { public int count() {
return this.count; return getItem().getAmount();
} }
public void count(int amount) { public void count(int amount) {
if (amount < 0) amount = 0; if (amount < 0) amount = 0;
this.count = amount; getItem().setAmount(amount);
} }
public Object getExactTag(Object... path) { public Object getExactTag(Object... path) {
@@ -75,9 +66,12 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
@Override @Override
public ItemStack load() { public ItemStack load() {
ItemStack itemStack = this.rtagItem.load(); return this.rtagItem.load();
itemStack.setAmount(Math.max(this.count, 0)); }
return itemStack;
@Override
public ItemStack getItem() {
return this.rtagItem.getItem();
} }
@Override @Override
@@ -87,6 +81,13 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
@Override @Override
public ItemWrapper<ItemStack> copyWithCount(int count) { public ItemWrapper<ItemStack> copyWithCount(int count) {
return new LegacyItemWrapper(new RtagItem(this.rtagItem.loadCopy()), count); ItemStack copied = this.rtagItem.loadCopy();
copied.setAmount(count);
return new LegacyItemWrapper(new RtagItem(copied));
}
@Override
public void shrink(int amount) {
this.count(count() - amount);
} }
} }

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.item; package net.momirealms.craftengine.bukkit.item;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.Item;
@@ -25,11 +26,25 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@SuppressWarnings("DuplicatedCode")
public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> { public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
@Override @Override
public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) { public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) {
if (!wrapped.hasTag(NETWORK_ITEM_TAG)) return Optional.empty(); Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
boolean hasDifferentMaterial = false;
if (optionalCustomItem.isPresent()) {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count());
hasDifferentMaterial = true;
}
}
if (!wrapped.hasTag(NETWORK_ITEM_TAG)) {
if (hasDifferentMaterial) {
return Optional.of(wrapped);
}
}
CompoundTag networkData = (CompoundTag) wrapped.getNBTTag(NETWORK_ITEM_TAG); CompoundTag networkData = (CompoundTag) wrapped.getNBTTag(NETWORK_ITEM_TAG);
if (networkData == null) return Optional.empty(); if (networkData == null) return Optional.empty();
wrapped.removeTag(NETWORK_ITEM_TAG); wrapped.removeTag(NETWORK_ITEM_TAG);
@@ -46,12 +61,16 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem(); Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) { if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) return Optional.empty(); if (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped).process(); return new OtherItem(wrapped, false).process();
} else { } else {
CustomItem<ItemStack> customItem = optionalCustomItem.get(); BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
boolean hasDifferentMaterial = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()) != customItem.clientItem();
if (hasDifferentMaterial) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
}
if (!customItem.hasClientBoundDataModifier()) { if (!customItem.hasClientBoundDataModifier()) {
if (!Config.interceptItem()) return Optional.empty(); if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
return new OtherItem(wrapped).process(); return new OtherItem(wrapped, hasDifferentMaterial).process();
} else { } else {
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
Tag argumentTag = wrapped.getNBTTag(ArgumentModifier.ARGUMENTS_TAG); Tag argumentTag = wrapped.getNBTTag(ArgumentModifier.ARGUMENTS_TAG);
@@ -77,7 +96,12 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
processLore(wrapped, tag::put); processLore(wrapped, tag::put);
} }
} }
if (tag.isEmpty()) return Optional.empty(); if (tag.isEmpty()) {
if (hasDifferentMaterial) {
return Optional.of(wrapped);
}
return Optional.empty();
}
wrapped.setTag(tag, NETWORK_ITEM_TAG); wrapped.setTag(tag, NETWORK_ITEM_TAG);
return Optional.of(wrapped); return Optional.of(wrapped);
} }
@@ -130,9 +154,11 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
private final Item<ItemStack> item; private final Item<ItemStack> item;
private boolean globalChanged = false; private boolean globalChanged = false;
private CompoundTag networkTag; private CompoundTag networkTag;
private final boolean forceReturn;
public OtherItem(Item<ItemStack> item) { public OtherItem(Item<ItemStack> item, boolean forceReturn) {
this.item = item; this.item = item;
this.forceReturn = forceReturn;
} }
public Optional<Item<ItemStack>> process() { public Optional<Item<ItemStack>> process() {
@@ -145,6 +171,8 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
if (this.globalChanged) { if (this.globalChanged) {
this.item.setTag(this.networkTag, NETWORK_ITEM_TAG); this.item.setTag(this.networkTag, NETWORK_ITEM_TAG);
return Optional.of(this.item); return Optional.of(this.item);
} else if (this.forceReturn) {
return Optional.of(this.item);
} else { } else {
return Optional.empty(); return Optional.empty();
} }

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.item; package net.momirealms.craftengine.bukkit.item;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.modifier.ArgumentModifier; import net.momirealms.craftengine.core.item.modifier.ArgumentModifier;
@@ -30,8 +31,22 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) { public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) {
Tag customData = wrapped.getNBTComponent(ComponentTypes.CUSTOM_DATA); Tag customData = wrapped.getNBTComponent(ComponentTypes.CUSTOM_DATA);
if (!(customData instanceof CompoundTag compoundTag)) return Optional.empty(); if (!(customData instanceof CompoundTag compoundTag)) return Optional.empty();
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
boolean hasDifferentMaterial = false;
if (optionalCustomItem.isPresent()) {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count());
hasDifferentMaterial = true;
}
}
CompoundTag networkData = compoundTag.getCompound(NETWORK_ITEM_TAG); CompoundTag networkData = compoundTag.getCompound(NETWORK_ITEM_TAG);
if (networkData == null) return Optional.empty(); if (networkData == null) {
if (hasDifferentMaterial) {
return Optional.of(wrapped);
}
return Optional.empty();
}
compoundTag.remove(NETWORK_ITEM_TAG); compoundTag.remove(NETWORK_ITEM_TAG);
for (Map.Entry<String, Tag> entry : networkData.entrySet()) { for (Map.Entry<String, Tag> entry : networkData.entrySet()) {
if (entry.getValue() instanceof CompoundTag tag) { if (entry.getValue() instanceof CompoundTag tag) {
@@ -48,12 +63,16 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem(); Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) { if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) return Optional.empty(); if (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped).process(); return new OtherItem(wrapped, false).process();
} else { } else {
CustomItem<ItemStack> customItem = optionalCustomItem.get(); BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
boolean hasDifferentMaterial = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()) != customItem.clientItem();
if (hasDifferentMaterial) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
}
if (!customItem.hasClientBoundDataModifier()) { if (!customItem.hasClientBoundDataModifier()) {
if (!Config.interceptItem()) return Optional.empty(); if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
return new OtherItem(wrapped).process(); return new OtherItem(wrapped, hasDifferentMaterial).process();
} else { } else {
CompoundTag customData = Optional.ofNullable(wrapped.getNBTComponent(ComponentTypes.CUSTOM_DATA)).map(CompoundTag.class::cast).orElse(new CompoundTag()); CompoundTag customData = Optional.ofNullable(wrapped.getNBTComponent(ComponentTypes.CUSTOM_DATA)).map(CompoundTag.class::cast).orElse(new CompoundTag());
CompoundTag arguments = customData.getCompound(ArgumentModifier.ARGUMENTS_TAG); CompoundTag arguments = customData.getCompound(ArgumentModifier.ARGUMENTS_TAG);
@@ -86,7 +105,10 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
else processLegacyLore(wrapped, () -> tag); else processLegacyLore(wrapped, () -> tag);
} }
} }
if (tag.isEmpty()) return Optional.empty(); if (tag.isEmpty()) {
if (hasDifferentMaterial) return Optional.of(wrapped);
return Optional.empty();
}
customData.put(NETWORK_ITEM_TAG, tag); customData.put(NETWORK_ITEM_TAG, tag);
wrapped.setNBTComponent(ComponentTypes.CUSTOM_DATA, customData); wrapped.setNBTComponent(ComponentTypes.CUSTOM_DATA, customData);
return Optional.of(wrapped); return Optional.of(wrapped);
@@ -203,11 +225,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
static class OtherItem { static class OtherItem {
private final Item<ItemStack> item; private final Item<ItemStack> item;
private final boolean forceReturn;
private boolean globalChanged = false; private boolean globalChanged = false;
private CompoundTag tag; private CompoundTag tag;
public OtherItem(Item<ItemStack> item) { public OtherItem(Item<ItemStack> item, boolean forceReturn) {
this.item = item; this.item = item;
this.forceReturn = forceReturn;
} }
public Optional<Item<ItemStack>> process() { public Optional<Item<ItemStack>> process() {
@@ -231,6 +255,8 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
customData.put(NETWORK_ITEM_TAG, getOrCreateTag()); customData.put(NETWORK_ITEM_TAG, getOrCreateTag());
this.item.setNBTComponent(ComponentKeys.CUSTOM_DATA, customData); this.item.setNBTComponent(ComponentKeys.CUSTOM_DATA, customData);
return Optional.of(this.item); return Optional.of(this.item);
} else if (this.forceReturn) {
return Optional.of(this.item);
} else { } else {
return Optional.empty(); return Optional.empty();
} }

View File

@@ -10,7 +10,8 @@ public class BukkitItemBehaviors extends ItemBehaviors {
public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item"); public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item");
public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item"); public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item");
public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item"); public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item");
public static final Key HAT_ITEM = Key.from("craftengine:hat_item"); public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item");
public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item");
public static void init() { public static void init() {
register(EMPTY, EmptyItemBehavior.FACTORY); register(EMPTY, EmptyItemBehavior.FACTORY);
@@ -19,5 +20,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY); register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY);
register(BUCKET_ITEM, BucketItemBehavior.FACTORY); register(BUCKET_ITEM, BucketItemBehavior.FACTORY);
register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY);
register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY);
} }
} }

View File

@@ -0,0 +1,80 @@
package net.momirealms.craftengine.bukkit.item.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.EventUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.WorldEvents;
import org.bukkit.GameEvent;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Levelled;
import org.bukkit.entity.Entity;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.util.Vector;
import java.nio.file.Path;
import java.util.Map;
public class CompostableItemBehavior extends ItemBehavior {
public static final Factory FACTORY = new Factory();
private final double chance;
public CompostableItemBehavior(double chance) {
this.chance = chance;
}
@Override
public InteractionResult useOnBlock(UseOnContext context) {
BukkitBlockInWorld block = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
BlockData blockData = block.block().getBlockData();
Object blockOwner = BlockStateUtils.getBlockOwner(BlockStateUtils.blockDataToBlockState(blockData));
if (blockOwner != MBlocks.COMPOSTER) return InteractionResult.PASS;
if (!(blockData instanceof Levelled levelled)) {
return InteractionResult.PASS;
}
int maxLevel = levelled.getMaximumLevel();
int currentLevel = levelled.getLevel();
if (currentLevel >= maxLevel) return InteractionResult.PASS;
boolean willRaise = (currentLevel == 0) && (this.chance > 0) || (RandomUtils.generateRandomDouble(0, 1) < this.chance);
if (willRaise) {
levelled.setLevel(currentLevel + 1);
EntityChangeBlockEvent event = new EntityChangeBlockEvent((Entity) context.getPlayer().platformPlayer(), block.block(), levelled);
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.FAIL;
}
block.block().setBlockData(levelled);
}
context.getLevel().levelEvent(WorldEvents.COMPOSTER_COMPOSTS, context.getClickedPos(), willRaise ? 1 : 0);
((World) context.getLevel().platformWorld()).sendGameEvent((Entity) context.getPlayer().platformPlayer(), GameEvent.BLOCK_CHANGE, new Vector(block.x() + 0.5, block.y() + 0.5, block.z() + 0.5));
if (currentLevel + 1 == 7) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), blockOwner, 20);
}
if (!context.getPlayer().canInstabuild()) {
context.getItem().shrink(1);
}
context.getPlayer().swingHand(context.getHand());
return InteractionResult.SUCCESS;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
double chance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("chance", 0.55), "chance");
return new CompostableItemBehavior(chance);
}
}
}

View File

@@ -0,0 +1,167 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
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.bukkit.util.InteractUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.nio.file.Path;
import java.util.Map;
public class FlintAndSteelItemBehavior extends ItemBehavior {
public static final FlintAndSteelItemBehavior INSTANCE = new FlintAndSteelItemBehavior();
public static final Factory FACTORY = new Factory();
private static final Key FLINT_SOUND = Key.of("item.flintandsteel.use");
@SuppressWarnings("unchecked")
@Override
public InteractionResult useOnBlock(UseOnContext context) {
BlockPos clickedPos = context.getClickedPos();
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(clickedPos);
Block block = clicked.block();
BlockPos firePos = clickedPos.relative(context.getClickedFace());
Direction direction = context.getHorizontalDirection();
// 最基础的判断能不能着火,不能着火都是扯蛋
try {
if (!(boolean) CoreReflections.method$BaseFireBlock$canBePlacedAt.invoke(null, context.getLevel().serverWorld(), LocationUtils.toBlockPos(firePos), DirectionUtils.toNMSDirection(direction))) {
return InteractionResult.PASS;
}
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to call BaseFireBlock$canBePlacedAt", e);
return InteractionResult.PASS;
}
// 判断点击的方块是否可燃
BlockData clickedBlockData = block.getBlockData();
Object clickedBlockState = BlockStateUtils.blockDataToBlockState(clickedBlockData);
boolean isClickedBlockBurnable;
try {
isClickedBlockBurnable = BlockStateUtils.isBurnable(clickedBlockState) ||
(context.getClickedFace() == Direction.UP && (boolean) CoreReflections.method$BlockStateBase$isFaceSturdy.invoke(
clickedBlockState, context.getLevel().serverWorld(), LocationUtils.toBlockPos(clickedPos), CoreReflections.instance$Direction$UP, CoreReflections.instance$SupportType$FULL));
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to call method$BlockStateBase$isFaceSturdy", e);
return InteractionResult.PASS;
}
net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer();
// 点击对象直接可燃,则忽略
if (isClickedBlockBurnable) {
int stateId = BlockStateUtils.blockStateToId(clickedBlockState);
if (BlockStateUtils.isVanillaBlock(stateId)) {
return InteractionResult.PASS;
} else {
// 点击对象为自定义方块
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
// 原版外观也可燃
if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().handle())) {
return InteractionResult.PASS;
}
BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle());
// 点击的是方块上面则只需要判断shift和可交互
if (direction == Direction.UP) {
// 客户端层面必须可交互
if (!InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState,
context.getHitResult(), (Item<ItemStack>) context.getItem())) {
return InteractionResult.PASS;
}
// 且没有shift
if (!player.isSecondaryUseActive()) {
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
}
} else {
// 玩家觉得自定义方块不可燃,且点击了侧面,那么就要判断火源下方的方块是否可燃,如果不可燃,则补发声音
BlockPos belowFirePos = firePos.relative(Direction.DOWN);
BukkitBlockInWorld belowFireBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(belowFirePos);
boolean belowCanBurn;
try {
Block belowBlock = belowFireBlock.block();
belowCanBurn = BlockStateUtils.isBurnable(BlockStateUtils.blockDataToBlockState(belowBlock.getBlockData())) ||
(boolean) CoreReflections.method$BlockStateBase$isFaceSturdy.invoke(
BlockStateUtils.blockDataToBlockState(belowFireBlock.block().getBlockData()), context.getLevel().serverWorld(), LocationUtils.toBlockPos(belowFirePos), CoreReflections.instance$Direction$UP, CoreReflections.instance$SupportType$FULL);
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to call method$BlockStateBase$isFaceSturdy", e);
return InteractionResult.PASS;
}
// 客户端觉得这玩意可交互,就会忽略声音
if (InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState, context.getHitResult(), (Item<ItemStack>) context.getItem())) {
// 如果按住了shift则代表尝试对侧面方块点火
if (player.isSecondaryUseActive()) {
// 如果底部不能燃烧,则燃烧点位为侧面,需要补发
if (!belowCanBurn) {
player.playSound(FLINT_SOUND, firePos, 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));
}
} else {
// 如果底部方块不可燃烧才补发
if (!belowCanBurn) {
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
player.swingHand(context.getHand());
}
}
}
}
} else {
// 如果点击的方块不可燃烧,但是服务端却认为可以放置火源,则可燃烧的方块一定位于火源的六个方向之一。
Direction relativeDirection = direction.opposite();
for (Direction dir : Direction.values()) {
if (dir == relativeDirection) continue;
BlockPos relPos = firePos.relative(dir);
BukkitBlockInWorld nearByBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(relPos);
BlockData nearbyBlockData = nearByBlock.block().getBlockData();
Object nearbyBlockState = BlockStateUtils.blockDataToBlockState(nearbyBlockData);
int stateID = BlockStateUtils.blockStateToId(nearbyBlockState);
if (BlockStateUtils.isVanillaBlock(stateID)) {
if (BlockStateUtils.isBurnable(nearbyBlockState)) {
return InteractionResult.PASS;
}
try {
if (dir == Direction.DOWN && (boolean) CoreReflections.method$BlockStateBase$isFaceSturdy.invoke(
nearbyBlockState, context.getLevel().serverWorld(), LocationUtils.toBlockPos(relPos), CoreReflections.instance$Direction$UP, CoreReflections.instance$SupportType$FULL)) {
return InteractionResult.PASS;
}
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to call method$BlockStateBase$isFaceSturdy", e);
return InteractionResult.PASS;
}
}
}
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
player.swingHand(context.getHand());
}
return InteractionResult.PASS;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -2,12 +2,13 @@ package net.momirealms.craftengine.bukkit.item.factory;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.saicone.rtag.item.ItemTagStream; import com.saicone.rtag.item.ItemTagStream;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.ItemTags; import net.momirealms.craftengine.bukkit.util.ItemTags;
import net.momirealms.craftengine.core.item.EquipmentData;
import net.momirealms.craftengine.core.item.ItemFactory; import net.momirealms.craftengine.core.item.ItemFactory;
import net.momirealms.craftengine.core.item.ItemWrapper; import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.item.JukeboxPlayable; import net.momirealms.craftengine.core.item.JukeboxPlayable;
import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.Tag;

View File

@@ -4,11 +4,16 @@ import com.google.gson.JsonElement;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.bukkit.nms.FastNMS; 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.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.EnchantmentUtils; import net.momirealms.craftengine.bukkit.util.EnchantmentUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.Enchantment; import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.Trim; import net.momirealms.craftengine.core.item.Trim;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -38,29 +43,136 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
return new ComponentItemWrapper(item); return new ComponentItemWrapper(item);
} }
@SuppressWarnings("unchecked")
@Override @Override
protected Object getJavaTag(ComponentItemWrapper item, Object... path) { protected Object getJavaTag(ComponentItemWrapper item, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); Map<String, Object> rootMap = (Map<String, Object>) item.getJavaComponent(ComponentTypes.CUSTOM_DATA).orElse(null);
if (rootMap == null) return null;
Object currentObj = rootMap;
for (int i = 0; i < path.length; i++) {
Object pathSegment = path[i];
if (pathSegment == null) return null;
String key = pathSegment.toString();
currentObj = ((Map<String, Object>) currentObj).get(key);
if (currentObj == null) return null;
if (i == path.length - 1) {
return currentObj;
}
if (!(currentObj instanceof Map)) {
return null;
}
}
return currentObj;
} }
@Override @Override
protected Tag getNBTTag(ComponentItemWrapper item, Object... path) { protected Tag getNBTTag(ComponentItemWrapper item, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(null);
if (rootTag == null) return null;
Tag currentTag = rootTag;
for (int i = 0; i < path.length; i++) {
Object pathSegment = path[i];
if (pathSegment == null) return null;
CompoundTag t = (CompoundTag) currentTag;
String key = pathSegment.toString();
currentTag = t.get(key);
if (currentTag == null) return null;
if (i == path.length - 1) {
return currentTag;
}
if (!(currentTag instanceof CompoundTag)) {
return null;
}
}
return currentTag;
} }
@Override @Override
protected void setTag(ComponentItemWrapper item, Object value, Object... path) { protected void setTag(ComponentItemWrapper item, Object value, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); Tag valueTag;
if (value instanceof Tag tag) {
valueTag = tag;
} else if (value instanceof JsonElement je) {
valueTag = MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, je);
} else if (CoreReflections.clazz$Tag.isInstance(value)) {
valueTag = MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, value);
} else {
assert MRegistryOps.JAVA != null;
valueTag = MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, value);
}
CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(new CompoundTag());
if (path == null || path.length == 0) {
if (valueTag instanceof CompoundTag) {
rootTag = (CompoundTag) valueTag;
} else {
throw new IllegalArgumentException("Cannot set non-CompoundTag as root without path");
}
} else {
CompoundTag currentTag = rootTag;
for (int i = 0; i < path.length - 1; i++) {
Object pathSegment = path[i];
if (pathSegment == null) throw new NullPointerException("Path segment cannot be null");
String key = pathSegment.toString();
Tag nextTag = currentTag.get(key);
if (!(nextTag instanceof CompoundTag)) {
nextTag = new CompoundTag();
currentTag.put(key, nextTag);
}
currentTag = (CompoundTag) nextTag;
}
String finalKey = path[path.length - 1].toString();
currentTag.put(finalKey, valueTag);
}
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag);
} }
@Override @Override
protected boolean hasTag(ComponentItemWrapper item, Object... path) { protected boolean hasTag(ComponentItemWrapper item, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); return getNBTTag(item, path) != null;
} }
@Override @Override
protected boolean removeTag(ComponentItemWrapper item, Object... path) { protected boolean removeTag(ComponentItemWrapper item, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(null);
if (rootTag == null || path == null || path.length == 0) return false;
if (path.length == 1) {
String key = path[0].toString();
if (rootTag.containsKey(key)) {
rootTag.remove(key);
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag);
return true;
}
return false;
}
CompoundTag parentTag = rootTag;
for (int i = 0; i < path.length - 1; i++) {
Object pathSegment = path[i];
if (pathSegment == null) return false;
String key = pathSegment.toString();
Tag childTag = parentTag.get(key);
if (!(childTag instanceof CompoundTag)) {
return false;
}
parentTag = (CompoundTag) childTag;
}
String finalKey = path[path.length - 1].toString();
if (parentTag.containsKey(finalKey)) {
parentTag.remove(finalKey);
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag);
return true;
}
return false;
} }
@Override @Override
@@ -397,9 +509,9 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
protected ComponentItemWrapper mergeCopy(ComponentItemWrapper item1, ComponentItemWrapper item2) { protected ComponentItemWrapper mergeCopy(ComponentItemWrapper item1, ComponentItemWrapper item2) {
Object itemStack1 = item1.getLiteralObject(); Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject(); Object itemStack2 = item2.getLiteralObject();
Object itemStack3 = FastNMS.INSTANCE.method$ItemStack$transmuteCopy(itemStack1, itemStack2); Object itemStack3 = FastNMS.INSTANCE.method$ItemStack$transmuteCopy(itemStack1, FastNMS.INSTANCE.method$ItemStack$getItem(itemStack2), item2.count());
FastNMS.INSTANCE.method$ItemStack$applyComponents(itemStack3, FastNMS.INSTANCE.method$ItemStack$getComponentsPatch(itemStack2)); FastNMS.INSTANCE.method$ItemStack$applyComponents(itemStack3, FastNMS.INSTANCE.method$ItemStack$getComponentsPatch(itemStack2));
return new ComponentItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack3), item2.count()); return new ComponentItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack3));
} }
@Override @Override
@@ -409,7 +521,21 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
try { try {
FastNMS.INSTANCE.method$ItemStack$applyComponents(itemStack1, FastNMS.INSTANCE.method$ItemStack$getComponentsPatch(itemStack2)); FastNMS.INSTANCE.method$ItemStack$applyComponents(itemStack1, FastNMS.INSTANCE.method$ItemStack$getComponentsPatch(itemStack2));
} catch (Exception e) { } catch (Exception e) {
plugin.logger().warn("Failed to merge item", e); this.plugin.logger().warn("Failed to merge item", e);
} }
} }
@Override
protected ComponentItemWrapper transmuteCopy(ComponentItemWrapper item, Key newItem, int amount) {
Object itemStack1 = item.getLiteralObject();
Object itemStack2 = FastNMS.INSTANCE.method$ItemStack$transmuteCopy(itemStack1, FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(newItem)), amount);
return new ComponentItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack2));
}
@Override
protected ComponentItemWrapper unsafeTransmuteCopy(ComponentItemWrapper item, Object newItem, int amount) {
Object itemStack1 = item.getLiteralObject();
Object itemStack2 = FastNMS.INSTANCE.method$ItemStack$transmuteCopy(itemStack1, newItem, amount);
return new ComponentItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack2));
}
} }

View File

@@ -2,7 +2,7 @@ package net.momirealms.craftengine.bukkit.item.factory;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.core.item.EquipmentData; import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
import java.util.Optional; import java.util.Optional;

View File

@@ -6,6 +6,9 @@ import com.saicone.rtag.tag.TagBase;
import com.saicone.rtag.tag.TagCompound; import com.saicone.rtag.tag.TagCompound;
import com.saicone.rtag.tag.TagList; import com.saicone.rtag.tag.TagList;
import net.momirealms.craftengine.bukkit.item.LegacyItemWrapper; import net.momirealms.craftengine.bukkit.item.LegacyItemWrapper;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.Enchantment; import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.Trim; import net.momirealms.craftengine.core.item.Trim;
import net.momirealms.craftengine.core.item.modifier.IdModifier; import net.momirealms.craftengine.core.item.modifier.IdModifier;
@@ -29,7 +32,7 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
@Override @Override
protected LegacyItemWrapper wrapInternal(ItemStack item) { protected LegacyItemWrapper wrapInternal(ItemStack item) {
return new LegacyItemWrapper(new RtagItem(item), item.getAmount()); return new LegacyItemWrapper(new RtagItem(item));
} }
@Override @Override
@@ -305,18 +308,32 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
@Override @Override
protected LegacyItemWrapper mergeCopy(LegacyItemWrapper item1, LegacyItemWrapper item2) { protected LegacyItemWrapper mergeCopy(LegacyItemWrapper item1, LegacyItemWrapper item2) {
Object itemStack = ItemObject.copy(item2.getLiteralObject()); Object itemStack = ItemObject.copy(item2.getLiteralObject());
ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject()))); FastNMS.INSTANCE.method$ItemStack$setTag(itemStack, TagCompound.clone(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item1.getLiteralObject())));
// one more step than vanilla // one more step than vanilla
TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true); TagCompound.merge(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(itemStack), FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item2.getLiteralObject()), true, true);
return new LegacyItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count()); return new LegacyItemWrapper(new RtagItem(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)));
} }
@Override @Override
protected void merge(LegacyItemWrapper item1, LegacyItemWrapper item2) { protected void merge(LegacyItemWrapper item1, LegacyItemWrapper item2) {
// load previous changes on nms items // load previous changes on nms items
item1.load(); item1.load();
TagCompound.merge(ItemObject.getCustomDataTag(item1.getLiteralObject()), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true); TagCompound.merge(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item1.getLiteralObject()), FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item2.getLiteralObject()), true, true);
// update wrapped item // update wrapped item
item1.update(); item1.update();
} }
@Override
protected LegacyItemWrapper transmuteCopy(LegacyItemWrapper item, Key newItem, int amount) {
Object newItemStack = FastNMS.INSTANCE.constructor$ItemStack(FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(newItem)), amount);
FastNMS.INSTANCE.method$ItemStack$setTag(newItemStack, TagCompound.clone(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item.getLiteralObject())));
return new LegacyItemWrapper(new RtagItem(ItemObject.asCraftMirror(newItemStack)));
}
@Override
protected LegacyItemWrapper unsafeTransmuteCopy(LegacyItemWrapper item, Object newItem, int amount) {
Object newItemStack = FastNMS.INSTANCE.constructor$ItemStack(newItem, amount);
FastNMS.INSTANCE.method$ItemStack$setTag(newItemStack, TagCompound.clone(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item.getLiteralObject())));
return new LegacyItemWrapper(new RtagItem(ItemObject.asCraftMirror(newItemStack)));
}
} }

View File

@@ -73,7 +73,7 @@ public class DebugStickListener implements Listener {
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(blockId))), true); ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(blockId))), true);
player.sendPacket(systemChatPacket, false); player.sendPacket(systemChatPacket, false);
} else { } else {
LegacyItemWrapper wrapped = new LegacyItemWrapper(new RtagItem(itemInHand), itemInHand.getAmount()); LegacyItemWrapper wrapped = new LegacyItemWrapper(new RtagItem(itemInHand));
Object storedData = wrapped.getJavaTag("craftengine:debug_stick_state"); Object storedData = wrapped.getJavaTag("craftengine:debug_stick_state");
if (storedData == null) storedData = new HashMap<>(); if (storedData == null) storedData = new HashMap<>();
if (storedData instanceof Map<?,?> map) { if (storedData instanceof Map<?,?> map) {

View File

@@ -15,12 +15,12 @@ import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.item.setting.FoodData;
import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.Vec3d;
@@ -28,11 +28,16 @@ import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
@@ -49,6 +54,28 @@ public class ItemEventListener implements Listener {
this.plugin = plugin; this.plugin = plugin;
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onInteractEntity(PlayerInteractEntityEvent event) {
BukkitServerPlayer serverPlayer = this.plugin.adapt(event.getPlayer());
if (serverPlayer == null) return;
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(hand);
if (itemInHand == null) return;
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
if (optionalCustomItem.isEmpty()) return;
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(event.getRightClicked().getLocation()))
.withParameter(DirectContextParameters.HAND, hand)
);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
customItem.execute(context, EventTrigger.RIGHT_CLICK);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onInteractBlock(PlayerInteractEvent event) { public void onInteractBlock(PlayerInteractEvent event) {
Action action = event.getAction(); Action action = event.getAction();
@@ -62,6 +89,7 @@ public class ItemEventListener implements Listener {
} }
BukkitServerPlayer serverPlayer = this.plugin.adapt(player); BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
if (serverPlayer == null) return;
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
// 如果本tick内主手已被处理则不处理副手 // 如果本tick内主手已被处理则不处理副手
// 这是因为客户端可能会同时发主副手交互包,但实际上只能处理其中一个 // 这是因为客户端可能会同时发主副手交互包,但实际上只能处理其中一个
@@ -318,18 +346,47 @@ public class ItemEventListener implements Listener {
if (optionalCustomItem.isEmpty()) { if (optionalCustomItem.isEmpty()) {
return; return;
} }
Cancellable dummy = Cancellable.dummy(); Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
CustomItem<ItemStack> customItem = optionalCustomItem.get(); CustomItem<ItemStack> customItem = optionalCustomItem.get();
PlayerOptionalContext context = PlayerOptionalContext.of(this.plugin.adapt(event.getPlayer()), ContextHolder.builder() PlayerOptionalContext context = PlayerOptionalContext.of(this.plugin.adapt(event.getPlayer()), ContextHolder.builder()
.withParameter(DirectContextParameters.ITEM_IN_HAND, wrapped) .withParameter(DirectContextParameters.ITEM_IN_HAND, wrapped)
.withParameter(DirectContextParameters.EVENT, dummy) .withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND) .withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND)
); );
customItem.execute(context, EventTrigger.CONSUME); customItem.execute(context, EventTrigger.CONSUME);
if (dummy.isCancelled()) { if (event.isCancelled()) {
event.setCancelled(true);
return; return;
} }
if (event.getPlayer().getGameMode() != GameMode.CREATIVE) {
Key replacement = customItem.settings().consumeReplacement();
if (replacement == null) {
event.setReplacement(null);
} else {
ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, this.plugin.adapt(event.getPlayer()));
event.setReplacement(replacementItem);
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void onFoodLevelChange(FoodLevelChangeEvent event) {
if (VersionHelper.isOrAbove1_20_5()) return;
if (!(event.getEntity() instanceof Player player)) return;
ItemStack consumedItem = event.getItem();
if (ItemUtils.isEmpty(consumedItem)) return;
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(consumedItem);
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) {
return;
}
CustomItem<ItemStack> customItem = optionalCustomItem.get();
FoodData foodData = customItem.settings().foodData();
if (foodData == null) return;
event.setCancelled(true);
int oldFoodLevel = player.getFoodLevel();
if (foodData.nutrition() != 0) player.setFoodLevel(MCUtils.clamp(oldFoodLevel + foodData.nutrition(), 0, 20));
float oldSaturation = player.getSaturation();
if (foodData.saturation() != 0) player.setSaturation(MCUtils.clamp(oldSaturation, 0, 10));
} }
private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) { private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) {
@@ -344,4 +401,17 @@ public class ItemEventListener implements Listener {
} }
return false; return false;
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityDamage(EntityDamageEvent event) {
if (event.getEntityType() == EntityType.ITEM && event.getEntity() instanceof org.bukkit.entity.Item item) {
Optional.ofNullable(this.plugin.itemManager().wrap(item.getItemStack()))
.flatMap(Item::getCustomItem)
.ifPresent(it -> {
if (it.settings().invulnerable().contains(DamageCauseUtils.fromBukkit(event.getCause()))) {
event.setCancelled(true);
}
});
}
}
} }

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; 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.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.MaterialUtils; import net.momirealms.craftengine.bukkit.util.MaterialUtils;
import net.momirealms.craftengine.bukkit.util.RecipeUtils; import net.momirealms.craftengine.bukkit.util.RecipeUtils;
@@ -45,9 +46,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// 将自定义配方转为“广义”配方,接受更加宽容的输入 // 将自定义配方转为“广义”配方,接受更加宽容的输入
// 部分过程借助bukkit完成部分直接通过nms方法注册 // 部分过程借助bukkit完成部分直接通过nms方法注册
private static final Map<Key, BukkitRecipeConvertor<? extends Recipe<ItemStack>>> MIXED_RECIPE_CONVERTORS = new HashMap<>(); private static final Map<Key, BukkitRecipeConvertor<? extends Recipe<ItemStack>>> MIXED_RECIPE_CONVERTORS = new HashMap<>();
private static Object nmsRecipeManager;
private static final List<Object> injectedIngredients = new ArrayList<>(); private static final List<Object> injectedIngredients = new ArrayList<>();
private static final IdentityHashMap<Recipe<ItemStack>, Object> recipeToMcRecipeHolder = new IdentityHashMap<>(); private static final IdentityHashMap<Recipe<ItemStack>, Object> CE_RECIPE_2_NMS_HOLDER = new IdentityHashMap<>();
private static Object nmsRecipeManager;
private static void registerNMSSmithingRecipe(Object recipe) { private static void registerNMSSmithingRecipe(Object recipe) {
try { try {
@@ -265,7 +266,8 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
} }
public Object nmsRecipeHolderByRecipe(Recipe<ItemStack> recipe) { public Object nmsRecipeHolderByRecipe(Recipe<ItemStack> recipe) {
return recipeToMcRecipeHolder.get(recipe); if (super.isReloading) return null;
return CE_RECIPE_2_NMS_HOLDER.get(recipe);
} }
public static Object nmsRecipeManager() { public static Object nmsRecipeManager() {
@@ -287,6 +289,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
@Override @Override
public void load() { public void load() {
if (!Config.enableRecipeSystem()) return; if (!Config.enableRecipeSystem()) return;
super.isReloading = true;
if (VersionHelper.isOrAbove1_21_2()) { if (VersionHelper.isOrAbove1_21_2()) {
try { try {
this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager); this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager);
@@ -308,7 +311,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to unregister recipes", e); this.plugin.logger().warn("Failed to unregister recipes", e);
} }
recipeToMcRecipeHolder.clear();
} }
@Override @Override
@@ -320,6 +322,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
@Override @Override
public void disable() { public void disable() {
unload(); unload();
CE_RECIPE_2_NMS_HOLDER.clear();
HandlerList.unregisterAll(this.recipeEventListener); HandlerList.unregisterAll(this.recipeEventListener);
if (this.crafterEventListener != null) { if (this.crafterEventListener != null) {
HandlerList.unregisterAll(this.crafterEventListener); HandlerList.unregisterAll(this.crafterEventListener);
@@ -473,6 +476,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// clear cache // clear cache
injectedIngredients.clear(); injectedIngredients.clear();
CE_RECIPE_2_NMS_HOLDER.clear();
// create mappings
for (Map.Entry<Key, Recipe<ItemStack>> entry : this.byId.entrySet()) {
Optional<Object> nmsRecipe = getOptionalNMSRecipe(entry.getKey());
nmsRecipe.ifPresent(o -> CE_RECIPE_2_NMS_HOLDER.put(entry.getValue(), o));
}
super.isReloading = false;
} catch (Exception e) { } catch (Exception e) {
this.plugin.logger().warn("Failed to run delayed recipe tasks", e); this.plugin.logger().warn("Failed to run delayed recipe tasks", e);
} }
@@ -691,7 +703,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
return optionalItem.map(itemStackCustomItem -> MaterialUtils.getMaterial(itemStackCustomItem.material())).orElse(null); return optionalItem.map(itemStackCustomItem -> MaterialUtils.getMaterial(itemStackCustomItem.material())).orElse(null);
} }
private static List<Object> getIngredientLooks(List<Holder<Key>> holders) throws ReflectiveOperationException { private static List<Object> getIngredientLooks(List<Holder<Key>> holders) {
List<Object> itemStacks = new ArrayList<>(); List<Object> itemStacks = new ArrayList<>();
for (Holder<Key> holder : holders) { for (Holder<Key> holder : holders) {
ItemStack itemStack = BukkitItemManager.instance().getBuildableItem(holder.value()).get().buildItemStack(ItemBuildContext.EMPTY, 1); ItemStack itemStack = BukkitItemManager.instance().getBuildableItem(holder.value()).get().buildItemStack(ItemBuildContext.EMPTY, 1);
@@ -710,8 +722,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
.map(Optional::get) .map(Optional::get)
.toList(); .toList();
Object shapedRecipe = getNMSRecipe(id); Object shapedRecipe = getOptionalNMSRecipe(id).get();
recipeToMcRecipeHolder.put(recipe, shapedRecipe);
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe); shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe);
} }
@@ -731,8 +742,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
try { try {
List<Ingredient<ItemStack>> actualIngredients = recipe.ingredientsInUse(); List<Ingredient<ItemStack>> actualIngredients = recipe.ingredientsInUse();
Object shapelessRecipe = getNMSRecipe(id); Object shapelessRecipe = getOptionalNMSRecipe(id).get();
recipeToMcRecipeHolder.put(recipe, shapelessRecipe);
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
shapelessRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapelessRecipe); shapelessRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapelessRecipe);
} }
@@ -751,8 +761,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
private static void injectCookingRecipe(Key id, CustomCookingRecipe<ItemStack> recipe) { private static void injectCookingRecipe(Key id, CustomCookingRecipe<ItemStack> recipe) {
try { try {
Ingredient<ItemStack> actualIngredient = recipe.ingredient(); Ingredient<ItemStack> actualIngredient = recipe.ingredient();
Object smeltingRecipe = getNMSRecipe(id); Object smeltingRecipe = getOptionalNMSRecipe(id).get();
recipeToMcRecipeHolder.put(recipe, smeltingRecipe);
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
smeltingRecipe = CoreReflections.field$RecipeHolder$recipe.get(smeltingRecipe); smeltingRecipe = CoreReflections.field$RecipeHolder$recipe.get(smeltingRecipe);
} }
@@ -771,23 +780,17 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// 获取nms配方请注意1.20.1获取配方本身而1.20.2+获取的是配方的holder // 获取nms配方请注意1.20.1获取配方本身而1.20.2+获取的是配方的holder
// recipe on 1.20.1 and holder on 1.20.2+ // recipe on 1.20.1 and holder on 1.20.2+
private static Object getNMSRecipe(Key id) throws ReflectiveOperationException { private static Optional<Object> getOptionalNMSRecipe(Key id) throws ReflectiveOperationException {
if (VersionHelper.isOrAbove1_21_2()) { if (VersionHelper.isOrAbove1_21_2()) {
Object resourceKey = CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, new NamespacedKey(id.namespace(), id.value())); Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey); Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey);
if (optional.isEmpty()) { return optional;
throw new IllegalArgumentException("Recipe " + id + " not found");
}
return optional.get();
} else { } else {
Object resourceLocation = KeyUtils.toResourceLocation(id); Object resourceLocation = KeyUtils.toResourceLocation(id);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation); Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation);
if (optional.isEmpty()) { return optional;
throw new IllegalArgumentException("Recipe " + id + " not found");
}
return optional.get();
} }
} }

View File

@@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.item.recipe.Recipe;
import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput;
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
import net.momirealms.craftengine.core.item.setting.AnvilRepairItem;
import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.BuiltInRegistries;
@@ -32,6 +33,7 @@ import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.Campfire; import org.bukkit.block.Campfire;
import org.bukkit.block.Furnace; import org.bukkit.block.Furnace;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
@@ -789,6 +791,49 @@ public class RecipeEventListener implements Listener {
return new Pair<>(first, second); return new Pair<>(first, second);
} }
// 不是完美的解决方案,仍然需要更多的探讨
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onCraft(CraftItemEvent event) {
org.bukkit.inventory.Recipe recipe = event.getRecipe();
if (!(recipe instanceof ShapelessRecipe) && !(recipe instanceof ShapedRecipe)) return;
HumanEntity humanEntity = event.getWhoClicked();
if (!(humanEntity instanceof Player player)) return;
CraftingInventory inventory = event.getInventory();
ItemStack result = inventory.getResult();
if (result == null) return;
ItemStack[] usedItems = inventory.getMatrix();
ItemStack[] replacements = new ItemStack[usedItems.length];
boolean hasReplacement = false;
for (int i = 0; i < usedItems.length; i++) {
ItemStack usedItem = usedItems[i];
if (ItemUtils.isEmpty(usedItem)) continue;
if (usedItem.getAmount() != 1) continue;
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(usedItem);
if (wrapped == null) continue;
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
Key remainingItem = customItem.settings().craftRemainder();
if (remainingItem != null) {
replacements[i] = BukkitItemManager.instance().buildItemStack(remainingItem, this.plugin.adapt(player));
hasReplacement = true;
}
}
}
if (!hasReplacement) return;
Runnable delayedTask = () -> {
for (int i = 0; i < replacements.length; i++) {
if (replacements[i] == null) continue;
inventory.setItem(i + 1, replacements[i]);
}
};
if (VersionHelper.isFolia()) {
player.getScheduler().run(this.plugin.javaPlugin(), (t) -> delayedTask.run(), () -> {});
} else {
this.plugin.scheduler().sync().runDelayed(delayedTask);
}
}
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onCraftingRecipe(PrepareItemCraftEvent event) { public void onCraftingRecipe(PrepareItemCraftEvent event) {
if (!Config.enableRecipeSystem()) return; if (!Config.enableRecipeSystem()) return;
@@ -843,7 +888,7 @@ public class RecipeEventListener implements Listener {
try { try {
player = (Player) CraftBukkitReflections.method$InventoryView$getPlayer.invoke(event.getView()); player = (Player) CraftBukkitReflections.method$InventoryView$getPlayer.invoke(event.getView());
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get inventory viewer", e); this.plugin.logger().warn("Failed to get inventory viewer", e);
return; return;
} }
@@ -854,14 +899,18 @@ public class RecipeEventListener implements Listener {
if (ceRecipe != null) { if (ceRecipe != null) {
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
serverPlayer.setLastUsedRecipe(ceRecipe.id()); serverPlayer.setLastUsedRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(recipeId)) {
correctCraftingRecipeUsed(inventory, ceRecipe); correctCraftingRecipeUsed(inventory, ceRecipe);
}
return; return;
} }
ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe); ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe);
if (ceRecipe != null) { if (ceRecipe != null) {
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
serverPlayer.setLastUsedRecipe(ceRecipe.id()); serverPlayer.setLastUsedRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(recipeId)) {
correctCraftingRecipeUsed(inventory, ceRecipe); correctCraftingRecipeUsed(inventory, ceRecipe);
}
return; return;
} }
// clear result if not met // clear result if not met
@@ -869,9 +918,8 @@ public class RecipeEventListener implements Listener {
} }
private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe<ItemStack> recipe) { private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe); Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) { if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return; return;
} }
try { try {
@@ -922,20 +970,21 @@ public class RecipeEventListener implements Listener {
CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe; CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe;
ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base)); ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base));
event.setResult(processed); event.setResult(processed);
if (!ceRecipe.id().equals(recipeId)) {
correctSmithingRecipeUsed(inventory, ceRecipe); correctSmithingRecipeUsed(inventory, ceRecipe);
} }
}
private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) { private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe); Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) { if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return; return;
} }
try { try {
Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory); Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory);
CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe); CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to correct used recipe", e); this.plugin.logger().warn("Failed to correct used recipe", e);
} }
} }

View File

@@ -53,7 +53,6 @@ import org.bukkit.plugin.java.JavaPlugin;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import java.io.*; import java.io.*;
import java.lang.reflect.Field;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.nio.file.Path; import java.nio.file.Path;
@@ -68,8 +67,6 @@ public class BukkitCraftEngine extends CraftEngine {
private SchedulerTask tickTask; private SchedulerTask tickTask;
private boolean successfullyLoaded = false; private boolean successfullyLoaded = false;
private boolean successfullyEnabled = false; private boolean successfullyEnabled = false;
private boolean requiresRestart = false;
private boolean hasMod = false;
private AntiGriefLib antiGrief; private AntiGriefLib antiGrief;
private JavaPlugin javaPlugin; private JavaPlugin javaPlugin;
private final Path dataFolderPath; private final Path dataFolderPath;
@@ -90,16 +87,6 @@ public class BukkitCraftEngine extends CraftEngine {
super.logger = logger; super.logger = logger;
super.platform = new BukkitPlatform(); super.platform = new BukkitPlatform();
super.scheduler = new BukkitSchedulerAdapter(this); super.scheduler = new BukkitSchedulerAdapter(this);
// find mod class if present
Class<?> modClass = ReflectionUtils.getClazz(MOD_CLASS);
if (modClass != null) {
Field isSuccessfullyRegistered = ReflectionUtils.getDeclaredField(modClass, "isSuccessfullyRegistered");
try {
requiresRestart = !(boolean) isSuccessfullyRegistered.get(null);
hasMod = true;
} catch (Exception ignore) {
}
}
Class<?> compatibilityClass = Objects.requireNonNull(ReflectionUtils.getClazz(COMPATIBILITY_CLASS), "Compatibility class not found"); Class<?> compatibilityClass = Objects.requireNonNull(ReflectionUtils.getClazz(COMPATIBILITY_CLASS), "Compatibility class not found");
try { try {
super.compatibilityManager = (CompatibilityManager) Objects.requireNonNull(ReflectionUtils.getConstructor(compatibilityClass, 0)).newInstance(this); super.compatibilityManager = (CompatibilityManager) Objects.requireNonNull(ReflectionUtils.getConstructor(compatibilityClass, 0)).newInstance(this);
@@ -132,7 +119,6 @@ public class BukkitCraftEngine extends CraftEngine {
if (super.blockManager == null) { if (super.blockManager == null) {
injectRegistries(); injectRegistries();
} }
if (this.requiresRestart) return;
try { try {
WorldStorageInjector.init(); WorldStorageInjector.init();
} catch (Exception e) { } catch (Exception e) {
@@ -177,17 +163,6 @@ public class BukkitCraftEngine extends CraftEngine {
return; return;
} }
this.successfullyEnabled = true; this.successfullyEnabled = true;
if (this.requiresRestart) {
logger().warn(" ");
logger().warn(" ");
logger().warn(" ");
logger().warn("This is the first time you have installed CraftEngine. A restart is required to apply the changes.");
logger().warn(" ");
logger().warn(" ");
logger().warn(" ");
Bukkit.getServer().shutdown();
return;
}
if (!this.successfullyLoaded) { if (!this.successfullyLoaded) {
logger().severe(" "); logger().severe(" ");
logger().severe(" "); logger().severe(" ");
@@ -390,14 +365,6 @@ public class BukkitCraftEngine extends CraftEngine {
); );
} }
public boolean hasMod() {
return hasMod;
}
public boolean requiresRestart() {
return requiresRestart;
}
public AntiGriefLib antiGrief() { public AntiGriefLib antiGrief() {
if (this.antiGrief == null) { if (this.antiGrief == null) {
this.antiGrief = AntiGriefLib.builder(this.javaPlugin) this.antiGrief = AntiGriefLib.builder(this.javaPlugin)

View File

@@ -1,12 +1,32 @@
package net.momirealms.craftengine.bukkit.plugin; package net.momirealms.craftengine.bukkit.plugin;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.Platform; import net.momirealms.craftengine.core.plugin.Platform;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import java.util.Map;
public class BukkitPlatform implements Platform { public class BukkitPlatform implements Platform {
@Override @Override
public void dispatchCommand(String command) { public void dispatchCommand(String command) {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command); Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command);
} }
@SuppressWarnings("unchecked")
@Override
public Object nbt2Java(String nbt) {
try {
Object tag = FastNMS.INSTANCE.method$TagParser$parseCompoundFully("{\"root\":" + nbt + "}");
Map<String, Object> map = (Map<String, Object>) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag);
return map.get("root");
} catch (CommandSyntaxException e) {
CraftEngine.instance().debug(e::getMessage);
throw new LocalizedResourceConfigException("warning.config.template.argument.default_value.invalid_syntax", e, nbt);
}
}
} }

View File

@@ -48,6 +48,8 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
new DebugSpawnFurnitureCommand(this, plugin), new DebugSpawnFurnitureCommand(this, plugin),
new DebugTargetBlockCommand(this, plugin), new DebugTargetBlockCommand(this, plugin),
new DebugIsSectionInjectedCommand(this, plugin), new DebugIsSectionInjectedCommand(this, plugin),
new DebugMigrateTemplatesCommand(this, plugin),
new DebugEntityId2UUIDCommand(this, plugin),
new TotemAnimationCommand(this, plugin), new TotemAnimationCommand(this, plugin),
new EnableResourceCommand(this, plugin), new EnableResourceCommand(this, plugin),
new DisableResourceCommand(this, plugin), new DisableResourceCommand(this, plugin),

View File

@@ -0,0 +1,59 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.incendo.cloud.Command;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.bukkit.parser.WorldParser;
import org.incendo.cloud.parser.standard.IntegerParser;
public class DebugEntityId2UUIDCommand extends BukkitCommandFeature<CommandSender> {
public DebugEntityId2UUIDCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
super(commandManager, plugin);
}
@Override
public Command.Builder<? extends CommandSender> assembleCommand(CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.required("world", WorldParser.worldParser())
.required("entityId", IntegerParser.integerParser())
.handler(context -> {
World world = context.get("world");
int entityId = context.get("entityId");
Entity entity = FastNMS.INSTANCE.getBukkitEntityById(world, entityId);
if (entity == null) {
context.sender().sendMessage("entity not found");
return;
}
Location location = entity.getLocation();
context.sender().sendMessage(
String.format(
"""
===========================
uuid: %s
name: %s
location: %s,%s,%s
type: %s
===========================
""",
entity.getUniqueId(),
entity.getName(),
location.x(), location.y(), location.z(),
entity.getType()
)
);
});
}
@Override
public String getFeatureID() {
return "debug_entity_id_to_uuid";
}
}

View File

@@ -0,0 +1,60 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.util.FileUtils;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.Command;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DebugMigrateTemplatesCommand extends BukkitCommandFeature<CommandSender> {
private static final Pattern PATTERN = Pattern.compile("(?<!\\$)\\{([^}]+)}");
public DebugMigrateTemplatesCommand(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
.handler(context -> {
for (Pack pack : BukkitCraftEngine.instance().packManager().loadedPacks()) {
for (Path file : FileUtils.getYmlConfigsDeeply(pack.configurationFolder())) {
try {
Files.writeString(file, replacePlaceholders(Files.readString(file)));
} catch (IOException e) {
e.printStackTrace();
}
}
}
context.sender().sendMessage("Done");
});
}
@Override
public String getFeatureID() {
return "debug_migrate_templates";
}
private static String replacePlaceholders(String input) {
if (input == null) {
return null;
}
Matcher matcher = PATTERN.matcher(input);
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
// 将 {xxx} 替换为 ${xxx}
matcher.appendReplacement(sb, "\\${" + matcher.group(1) + "}");
}
matcher.appendTail(sb);
return sb.toString();
}
}

View File

@@ -54,7 +54,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature<CommandSend
} }
Location location = context.get("location"); Location location = context.get("location");
CustomFurniture customFurniture = optionalCustomFurniture.get(); CustomFurniture customFurniture = optionalCustomFurniture.get();
AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyPlacement()); AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyAnchorType());
boolean playSound = context.flags().hasFlag("silent"); boolean playSound = context.flags().hasFlag("silent");
CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); CraftEngineFurniture.place(location, customFurniture, anchorType, playSound);
}); });

View File

@@ -53,7 +53,7 @@ public class DisableResourceCommand extends BukkitCommandFeature<CommandSender>
return; return;
} }
} }
YamlDocument document = plugin().config().loadYamlData(packMetaPath.toFile()); YamlDocument document = plugin().config().loadYamlData(packMetaPath);
document.set("enable", false); document.set("enable", false);
try { try {
document.save(packMetaPath.toFile()); document.save(packMetaPath.toFile());

View File

@@ -52,7 +52,7 @@ public class EnableResourceCommand extends BukkitCommandFeature<CommandSender> {
return; return;
} }
} }
YamlDocument document = plugin().config().loadYamlData(packMetaPath.toFile()); YamlDocument document = plugin().config().loadYamlData(packMetaPath);
document.set("enable", true); document.set("enable", true);
try { try {
document.save(packMetaPath.toFile()); document.save(packMetaPath.toFile());

View File

@@ -19,6 +19,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.NoteBlockChainUpdateUtils; import net.momirealms.craftengine.bukkit.util.NoteBlockChainUpdateUtils;
import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ObjectHolder; import net.momirealms.craftengine.core.util.ObjectHolder;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
@@ -36,6 +37,7 @@ public final class BlockGenerator {
private static Field field$CraftEngineBlock$behavior; private static Field field$CraftEngineBlock$behavior;
private static Field field$CraftEngineBlock$shape; private static Field field$CraftEngineBlock$shape;
private static Field field$CraftEngineBlock$isNoteBlock; private static Field field$CraftEngineBlock$isNoteBlock;
private static Field field$CraftEngineBlock$isTripwire;
public static void init() throws ReflectiveOperationException { public static void init() throws ReflectiveOperationException {
ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17);
@@ -48,6 +50,7 @@ public final class BlockGenerator {
.defineField("behaviorHolder", ObjectHolder.class, Visibility.PUBLIC) .defineField("behaviorHolder", ObjectHolder.class, Visibility.PUBLIC)
.defineField("shapeHolder", ObjectHolder.class, Visibility.PUBLIC) .defineField("shapeHolder", ObjectHolder.class, Visibility.PUBLIC)
.defineField("isClientSideNoteBlock", boolean.class, Visibility.PUBLIC) .defineField("isClientSideNoteBlock", boolean.class, Visibility.PUBLIC)
.defineField("isClientSideTripwire", boolean.class, Visibility.PUBLIC)
// should always implement this interface // should always implement this interface
.implement(CoreReflections.clazz$Fallable) .implement(CoreReflections.clazz$Fallable)
.implement(CoreReflections.clazz$BonemealableBlock) .implement(CoreReflections.clazz$BonemealableBlock)
@@ -55,13 +58,15 @@ public final class BlockGenerator {
// internal interfaces // internal interfaces
.implement(BehaviorHolder.class) .implement(BehaviorHolder.class)
.implement(ShapeHolder.class) .implement(ShapeHolder.class)
.implement(NoteBlockIndicator.class) .implement(ChainUpdateBlockIndicator.class)
.method(ElementMatchers.named("getBehaviorHolder")) .method(ElementMatchers.named("getBehaviorHolder"))
.intercept(FieldAccessor.ofField("behaviorHolder")) .intercept(FieldAccessor.ofField("behaviorHolder"))
.method(ElementMatchers.named("getShapeHolder")) .method(ElementMatchers.named("getShapeHolder"))
.intercept(FieldAccessor.ofField("shapeHolder")) .intercept(FieldAccessor.ofField("shapeHolder"))
.method(ElementMatchers.named("isNoteBlock")) .method(ElementMatchers.named("isNoteBlock"))
.intercept(FieldAccessor.ofField("isClientSideNoteBlock")) .intercept(FieldAccessor.ofField("isClientSideNoteBlock"))
.method(ElementMatchers.named("isTripwire"))
.intercept(FieldAccessor.ofField("isClientSideTripwire"))
// getShape // getShape
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getShape)) .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getShape))
.intercept(MethodDelegation.to(GetShapeInterceptor.INSTANCE)) .intercept(MethodDelegation.to(GetShapeInterceptor.INSTANCE))
@@ -142,6 +147,7 @@ public final class BlockGenerator {
field$CraftEngineBlock$behavior = clazz$CraftEngineBlock.getField("behaviorHolder"); field$CraftEngineBlock$behavior = clazz$CraftEngineBlock.getField("behaviorHolder");
field$CraftEngineBlock$shape = clazz$CraftEngineBlock.getField("shapeHolder"); field$CraftEngineBlock$shape = clazz$CraftEngineBlock.getField("shapeHolder");
field$CraftEngineBlock$isNoteBlock = clazz$CraftEngineBlock.getField("isClientSideNoteBlock"); field$CraftEngineBlock$isNoteBlock = clazz$CraftEngineBlock.getField("isClientSideNoteBlock");
field$CraftEngineBlock$isTripwire = clazz$CraftEngineBlock.getField("isClientSideTripwire");
} }
public static Object generateBlock(Key replacedBlock, Object ownerBlock, Object properties) throws Throwable { public static Object generateBlock(Key replacedBlock, Object ownerBlock, Object properties) throws Throwable {
@@ -155,19 +161,29 @@ public final class BlockGenerator {
field$CraftEngineBlock$behavior.set(newBlockInstance, behaviorHolder); field$CraftEngineBlock$behavior.set(newBlockInstance, behaviorHolder);
field$CraftEngineBlock$shape.set(newBlockInstance, shapeHolder); field$CraftEngineBlock$shape.set(newBlockInstance, shapeHolder);
field$CraftEngineBlock$isNoteBlock.set(newBlockInstance, replacedBlock.equals(BlockKeys.NOTE_BLOCK)); field$CraftEngineBlock$isNoteBlock.set(newBlockInstance, replacedBlock.equals(BlockKeys.NOTE_BLOCK));
field$CraftEngineBlock$isTripwire.set(newBlockInstance, replacedBlock.equals(BlockKeys.TRIPWIRE));
return newBlockInstance; return newBlockInstance;
} }
public static class UpdateShapeInterceptor { public static class UpdateShapeInterceptor {
public static final UpdateShapeInterceptor INSTANCE = new UpdateShapeInterceptor(); public static final UpdateShapeInterceptor INSTANCE = new UpdateShapeInterceptor();
public static final int levelIndex = VersionHelper.isOrAbove1_21_2() ? 1 : 3; public static final int levelIndex = VersionHelper.isOrAbove1_21_2() ? 1 : 3;
public static final int directionIndex = VersionHelper.isOrAbove1_21_2() ? 4 : 1;
public static final int posIndex = VersionHelper.isOrAbove1_21_2() ? 3 : 4;
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder(); ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
if (((NoteBlockIndicator) thisObj).isNoteBlock() && CoreReflections.clazz$ServerLevel.isInstance(args[levelIndex])) { ChainUpdateBlockIndicator indicator = (ChainUpdateBlockIndicator) thisObj;
if (indicator.isNoteBlock()) {
if (CoreReflections.clazz$ServerLevel.isInstance(args[levelIndex])) {
startNoteBlockChain(args); startNoteBlockChain(args);
} }
} else if (indicator.isTripwire()) {
if (CoreReflections.clazz$ServerLevel.isInstance(args[posIndex])) {
}
}
try { try {
return holder.value().updateShape(thisObj, args, superMethod); return holder.value().updateShape(thisObj, args, superMethod);
} catch (Exception e) { } catch (Exception e) {
@@ -175,30 +191,20 @@ public final class BlockGenerator {
return args[0]; return args[0];
} }
} }
}
private static void startNoteBlockChain(Object[] args) throws ReflectiveOperationException { private static void startNoteBlockChain(Object[] args) {
Object direction; Object direction = args[directionIndex];
Object serverLevel; Object serverLevel = args[levelIndex];
Object blockPos; Object blockPos = args[posIndex];
if (VersionHelper.isOrAbove1_21_2()) {
direction = args[4];
serverLevel = args[1];
blockPos = args[3];
} else {
direction = args[1];
serverLevel = args[3];
blockPos = args[4];
}
int id = (int) CoreReflections.field$Direction$data3d.get(direction);
// Y axis // Y axis
if (id == 0 || id == 1) { if (direction == CoreReflections.instance$Direction$DOWN) {
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos); FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
if (id == 1) { NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, Config.maxNoteBlockChainUpdate());
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$DOWN, blockPos, 0); } else if (direction == CoreReflections.instance$Direction$UP) {
} else { Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, 0); FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$DOWN, blockPos, Config.maxNoteBlockChainUpdate());
} }
} }
} }

View File

@@ -8,6 +8,10 @@ public interface InjectedCacheCheck {
void recipeType(Object recipeType); void recipeType(Object recipeType);
Key customRecipeType();
void customRecipeType(Key customRecipeType);
Object lastRecipe(); Object lastRecipe();
void lastRecipe(Object lastRecipe); void lastRecipe(Object lastRecipe);

View File

@@ -16,6 +16,8 @@ import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe; import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe;
import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem; import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem;
@@ -28,7 +30,6 @@ import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Optional; import java.util.Optional;
public class RecipeInjector { public class RecipeInjector {
@@ -41,17 +42,23 @@ public class RecipeInjector {
.name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker") .name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker")
.implement(CoreReflections.clazz$RecipeManager$CachedCheck) .implement(CoreReflections.clazz$RecipeManager$CachedCheck)
.implement(InjectedCacheCheck.class) .implement(InjectedCacheCheck.class)
.defineField("recipeType", Object.class, Visibility.PUBLIC) .defineField("recipeType", Object.class, Visibility.PUBLIC)
.method(ElementMatchers.named("recipeType")) .method(ElementMatchers.named("recipeType"))
.intercept(FieldAccessor.ofField("recipeType")) .intercept(FieldAccessor.ofField("recipeType"))
.defineField("customRecipeType", Key.class, Visibility.PUBLIC)
.method(ElementMatchers.named("customRecipeType"))
.intercept(FieldAccessor.ofField("customRecipeType"))
.defineField("lastRecipe", Object.class, Visibility.PUBLIC) .defineField("lastRecipe", Object.class, Visibility.PUBLIC)
.method(ElementMatchers.named("lastRecipe")) .method(ElementMatchers.named("lastRecipe"))
.intercept(FieldAccessor.ofField("lastRecipe")) .intercept(FieldAccessor.ofField("lastRecipe"))
.method(ElementMatchers.named("setLastRecipe"))
.intercept(FieldAccessor.ofField("lastRecipe"))
.defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC) .defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC)
.method(ElementMatchers.named("lastCustomRecipe")) .method(ElementMatchers.named("lastCustomRecipe"))
.intercept(FieldAccessor.ofField("lastCustomRecipe")) .intercept(FieldAccessor.ofField("lastCustomRecipe"))
.method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a"))) .method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a")))
.intercept(MethodDelegation.to( .intercept(MethodDelegation.to(
VersionHelper.isOrAbove1_21_2() ? VersionHelper.isOrAbove1_21_2() ?
@@ -73,50 +80,60 @@ public class RecipeInjector {
if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected
Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity); Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity);
InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker);
injectedChecker.recipeType(recipeType); if (recipeType == MRecipeTypes.SMELTING) {
injectedChecker.customRecipeType(RecipeTypes.SMELTING);
injectedChecker.recipeType(MRecipeTypes.SMELTING);
} else if (recipeType == MRecipeTypes.BLASTING) {
injectedChecker.customRecipeType(RecipeTypes.BLASTING);
injectedChecker.recipeType(MRecipeTypes.BLASTING);
} else if (recipeType == MRecipeTypes.SMOKING) {
injectedChecker.customRecipeType(RecipeTypes.SMOKING);
injectedChecker.recipeType(MRecipeTypes.SMOKING);
} else {
throw new IllegalStateException("RecipeType " + recipeType + " not supported");
}
CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker); CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker);
} else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) { } else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) {
Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity); Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity);
if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected
InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker);
injectedChecker.customRecipeType(RecipeTypes.CAMPFIRE_COOKING);
injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING); injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING);
CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker); CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker);
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_20 { public static class GetRecipeForMethodInterceptor1_20 {
public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20(); public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Object lastRecipe = injectedCacheCheck.lastRecipe(); Optional<Pair<Object, Object>> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
Optional<Pair<Object, Object>> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) { return Optional.empty();
Pair<Object, Object> pair = optionalRecipe.get(); }
Object resourceLocation = pair.getFirst();
Key recipeId = Key.of(resourceLocation.toString()); Pair<Object, Object> resourceLocationAndRecipe = optionalRecipe.get();
Object rawRecipeResourceLocation = resourceLocationAndRecipe.getFirst();
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack; boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
List<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]);
}
itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0));
// it's a recipe from other plugins
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) { if (!isCustom) {
injectedCacheCheck.lastRecipe(resourceLocation); injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return Optional.of(pair.getSecond()); return Optional.of(resourceLocationAndRecipe.getSecond());
} }
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(
injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ?
FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() :
FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0)
);
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack); Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) { if (idHolder.isEmpty()) {
@@ -124,64 +141,47 @@ public class RecipeInjector {
} }
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe; CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) { if (ceRecipe == null) {
return Optional.empty(); return Optional.empty();
} }
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(resourceLocation); injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond()));
} else {
return Optional.empty();
} }
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_20_5 { public static class GetRecipeForMethodInterceptor1_20_5 {
public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5(); public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Object lastRecipe = injectedCacheCheck.lastRecipe(); Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) { return Optional.empty();
Object holder = optionalRecipe.get();
Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder);
Key recipeId = Key.of(id.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack;
List<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]);
} }
itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0));
// it's a recipe from other plugins Object rawRecipeHolder = optionalRecipe.get();
boolean isCustom = recipeManager.isCustomRecipe(recipeId); Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder);
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(
injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ?
FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() :
FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0)
);
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) { if (!isCustom) {
injectedCacheCheck.lastRecipe(id); injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return optionalRecipe; return optionalRecipe;
} }
@@ -192,59 +192,45 @@ public class RecipeInjector {
} }
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe; CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) { if (ceRecipe == null) {
return Optional.empty(); return Optional.empty();
} }
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(id); injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
return Optional.empty();
} }
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_21 { public static class GetRecipeForMethodInterceptor1_21 {
public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21(); public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Object lastRecipe = injectedCacheCheck.lastRecipe(); Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) { return Optional.empty();
Object holder = optionalRecipe.get(); }
Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder);
Key recipeId = Key.of(id.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0]));
// it's a recipe from other plugins Object rawRecipeHolder = optionalRecipe.get();
boolean isCustom = recipeManager.isCustomRecipe(recipeId); Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder);
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) { if (!isCustom) {
injectedCacheCheck.lastRecipe(id); injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return optionalRecipe; return optionalRecipe;
} }
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0]));
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack); Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) { if (idHolder.isEmpty()) {
@@ -252,60 +238,49 @@ public class RecipeInjector {
} }
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe; CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) { if (ceRecipe == null) {
return Optional.empty(); return Optional.empty();
} }
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(id); injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
return Optional.empty();
} }
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_21_2 { public static class GetRecipeForMethodInterceptor1_21_2 {
public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2(); public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceKey = injectedCacheCheck.lastRecipe();
Object lastRecipe = injectedCacheCheck.lastRecipe(); Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey);
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) { return Optional.empty();
Object holder = optionalRecipe.get(); }
Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder);
Object resourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(id);
Key recipeId = Key.of(resourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0]));
// it's a recipe from other plugins // 获取配方的基础信息
boolean isCustom = recipeManager.isCustomRecipe(recipeId); Object recipeHolder = optionalRecipe.get();
Object rawRecipeResourceKey = FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolder);
Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(rawRecipeResourceKey);
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
// 来自其他插件注册的自定义配方
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) { if (!isCustom) {
injectedCacheCheck.lastRecipe(id); injectedCacheCheck.lastRecipe(rawRecipeResourceKey);
return optionalRecipe; return optionalRecipe;
} }
// 获取唯一内存地址id
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0]));
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack); Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) { if (idHolder.isEmpty()) {
@@ -313,29 +288,19 @@ public class RecipeInjector {
} }
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe; CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); // 这个ce配方并不存在那么应该返回空
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) { if (ceRecipe == null) {
return Optional.empty(); return Optional.empty();
} }
// Cache recipes, it might be incorrect on reloading // 记录上一次使用的配方(ce)
injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all // 更新上一次使用的配方(nms)
injectedCacheCheck.lastRecipe(id); if (!ceRecipe.id().equals(rawRecipeKey)) {
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); injectedCacheCheck.lastRecipe(FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(ceRecipe.id())));
} else { }
return Optional.empty(); return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
}
} }
} }
} }

View File

@@ -17,6 +17,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.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.EmptyBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -220,23 +221,36 @@ public class WorldStorageInjector {
CESection section = holder.ceSection(); CESection section = holder.ceSection();
// 如果是原版方块 // 如果是原版方块
if (BlockStateUtils.isVanillaBlock(stateId)) { if (BlockStateUtils.isVanillaBlock(stateId)) {
// 那么应该情况自定义块 // 那么应该清空自定义块
ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE);
// 如果先前不是空气则标记 // 处理 自定义块 -> 原版块
if (!previous.isEmpty()) { if (!previous.isEmpty()) {
holder.ceChunk().setDirty(true); holder.ceChunk().setDirty(true);
if (Config.enableLightSystem()) { if (Config.enableLightSystem()) {
updateLightIfChanged(holder, previousState, newState, newState, x, y, z); // 自定义块到原版块,只需要判断旧块是否和客户端一直
BlockStateWrapper wrapper = previous.vanillaBlockState();
if (wrapper != null) {
updateLight(holder, wrapper.handle(), previousState, x, y, z);
}
} }
} }
} else { } else {
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState); ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState);
if (previousImmutableBlockState == immutableBlockState) return; if (previousImmutableBlockState == immutableBlockState) return;
// 处理 自定义块到自定义块或原版块到自定义块
holder.ceChunk().setDirty(true); holder.ceChunk().setDirty(true);
// 不可能!绝对不可能!
if (immutableBlockState.isEmpty()) return;
// 如果新方块的光照属性和客户端认为的不同 // 如果新方块的光照属性和客户端认为的不同
if (Config.enableLightSystem() && !immutableBlockState.isEmpty()) { if (Config.enableLightSystem()) {
updateLightIfChanged(holder, previousState, immutableBlockState.vanillaBlockState().handle(), newState, x, y, z); if (previousImmutableBlockState.isEmpty()) {
// 原版块到自定义块,只需要判断新块是否和客户端视觉一致
updateLight(holder, immutableBlockState.vanillaBlockState().handle(), newState, x, y, z);
} else {
// 自定义块到自定义块
updateLight$complex(holder, immutableBlockState.vanillaBlockState().handle(), newState, previousState, x, y, z);
}
} }
} }
} catch (Exception e) { } catch (Exception e) {
@@ -244,17 +258,31 @@ public class WorldStorageInjector {
} }
} }
protected static void updateLightIfChanged(@This InjectedHolder thisObj, Object oldServerSideState, Object clientSideState, Object serverSideState, int x, int y, int z) { @SuppressWarnings("DuplicatedCode")
protected static void updateLight(@This InjectedHolder thisObj, Object clientState, Object serverState, int x, int y, int z) {
CEWorld world = thisObj.ceChunk().world(); CEWorld world = thisObj.ceChunk().world();
Object blockPos = LocationUtils.toBlockPos(x, y, z); Object blockPos = LocationUtils.toBlockPos(x, y, z);
Object serverWorld = world.world().serverWorld(); Object serverWorld = world.world().serverWorld();
if (clientSideState != serverSideState && FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(clientSideState, serverSideState, serverWorld, blockPos)) { if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(serverState, clientState, serverWorld, blockPos)) {
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);
}
}
@SuppressWarnings("DuplicatedCode")
protected 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)) {
SectionPos sectionPos = thisObj.cePos(); SectionPos sectionPos = thisObj.cePos();
List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
world.sectionLightUpdated(pos); world.sectionLightUpdated(pos);
return; return;
} }
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(oldServerSideState, serverSideState, serverWorld, blockPos)) { if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newServerState, oldServerState, serverWorld, blockPos)) {
SectionPos sectionPos = thisObj.cePos(); SectionPos sectionPos = thisObj.cePos();
List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
world.sectionLightUpdated(pos); world.sectionLightUpdated(pos);

View File

@@ -82,7 +82,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
private static final String CONNECTION_HANDLER_NAME = "craftengine_connection_handler"; private static final String CONNECTION_HANDLER_NAME = "craftengine_connection_handler";
private static final String SERVER_CHANNEL_HANDLER_NAME = "craftengine_server_channel_handler"; private static final String SERVER_CHANNEL_HANDLER_NAME = "craftengine_server_channel_handler";
private static final String PLAYER_CHANNEL_HANDLER_NAME = "craftengine_player_packet_handler"; private static final String PLAYER_CHANNEL_HANDLER_NAME = "craftengine_player_channel_handler";
private static final String PACKET_ENCODER = "craftengine_encoder"; private static final String PACKET_ENCODER = "craftengine_encoder";
private static final String PACKET_DECODER = "craftengine_decoder"; private static final String PACKET_DECODER = "craftengine_decoder";
@@ -147,9 +147,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.SET_CREATIVE_SLOT, NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket); registerNMSPacketConsumer(PacketConsumers.SET_CREATIVE_SLOT, NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket);
registerNMSPacketConsumer(PacketConsumers.LOGIN, NetworkReflections.clazz$ClientboundLoginPacket); registerNMSPacketConsumer(PacketConsumers.LOGIN, NetworkReflections.clazz$ClientboundLoginPacket);
registerNMSPacketConsumer(PacketConsumers.RESPAWN, NetworkReflections.clazz$ClientboundRespawnPacket); registerNMSPacketConsumer(PacketConsumers.RESPAWN, NetworkReflections.clazz$ClientboundRespawnPacket);
registerNMSPacketConsumer(PacketConsumers.INTERACT_ENTITY, NetworkReflections.clazz$ServerboundInteractPacket);
registerNMSPacketConsumer(PacketConsumers.SYNC_ENTITY_POSITION, NetworkReflections.clazz$ClientboundEntityPositionSyncPacket); registerNMSPacketConsumer(PacketConsumers.SYNC_ENTITY_POSITION, NetworkReflections.clazz$ClientboundEntityPositionSyncPacket);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, NetworkReflections.clazz$ServerboundPickItemFromEntityPacket); registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, NetworkReflections.clazz$ServerboundPickItemFromEntityPacket);
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, NetworkReflections.clazz$ServerboundRenameItemPacket); registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, NetworkReflections.clazz$ServerboundRenameItemPacket);
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, NetworkReflections.clazz$ServerboundSignUpdatePacket); registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, NetworkReflections.clazz$ServerboundSignUpdatePacket);
@@ -161,6 +159,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, NetworkReflections.clazz$ServerboundResourcePackPacket); registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, NetworkReflections.clazz$ServerboundResourcePackPacket);
registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, NetworkReflections.clazz$ClientboundEntityEventPacket); registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, NetworkReflections.clazz$ClientboundEntityEventPacket);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot); registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos);
registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
@@ -177,7 +176,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_OBJECTIVE_1_20_3 : PacketConsumers.SET_OBJECTIVE_1_20, this.packetIds.clientboundSetObjectivePacket()); registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_OBJECTIVE_1_20_3 : PacketConsumers.SET_OBJECTIVE_1_20, this.packetIds.clientboundSetObjectivePacket());
registerS2CByteBufPacketConsumer(PacketConsumers.SET_SCORE_1_20_3, VersionHelper.isOrAbove1_20_3() ? this.packetIds.clientboundSetScorePacket() : -1); registerS2CByteBufPacketConsumer(PacketConsumers.SET_SCORE_1_20_3, VersionHelper.isOrAbove1_20_3() ? this.packetIds.clientboundSetScorePacket() : -1);
registerS2CByteBufPacketConsumer(PacketConsumers.REMOVE_ENTITY, this.packetIds.clientboundRemoveEntitiesPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.REMOVE_ENTITY, this.packetIds.clientboundRemoveEntitiesPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.ADD_ENTITY_BYTEBUFFER, this.packetIds.clientboundAddEntityPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.ADD_ENTITY, this.packetIds.clientboundAddEntityPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.SOUND, this.packetIds.clientboundSoundPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SOUND, this.packetIds.clientboundSoundPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.SET_ENTITY_DATA, this.packetIds.clientboundSetEntityDataPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SET_ENTITY_DATA, this.packetIds.clientboundSetEntityDataPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.CONTAINER_SET_CONTENT, this.packetIds.clientboundContainerSetContentPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.CONTAINER_SET_CONTENT, this.packetIds.clientboundContainerSetContentPacket());
@@ -187,6 +186,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerS2CByteBufPacketConsumer(PacketConsumers.SET_PLAYER_INVENTORY_1_21_2, this.packetIds.clientboundSetPlayerInventoryPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SET_PLAYER_INVENTORY_1_21_2, this.packetIds.clientboundSetPlayerInventoryPacket());
registerC2SByteBufPacketConsumer(PacketConsumers.SET_CREATIVE_MODE_SLOT, this.packetIds.serverboundSetCreativeModeSlotPacket()); registerC2SByteBufPacketConsumer(PacketConsumers.SET_CREATIVE_MODE_SLOT, this.packetIds.serverboundSetCreativeModeSlotPacket());
registerC2SByteBufPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_20, this.packetIds.serverboundContainerClickPacket()); registerC2SByteBufPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_20, this.packetIds.serverboundContainerClickPacket());
registerC2SByteBufPacketConsumer(PacketConsumers.INTERACT_ENTITY, this.packetIds.serverboundInteractPacket());
} }
public static BukkitNetworkManager instance() { public static BukkitNetworkManager instance() {

View File

@@ -70,7 +70,6 @@ import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@@ -85,7 +84,7 @@ public class PacketConsumers {
public static void initEntities(int registrySize) { public static void initEntities(int registrySize) {
ADD_ENTITY_HANDLERS = new BukkitNetworkManager.Handlers[registrySize]; ADD_ENTITY_HANDLERS = new BukkitNetworkManager.Handlers[registrySize];
Arrays.fill(ADD_ENTITY_HANDLERS, BukkitNetworkManager.Handlers.DO_NOTHING); Arrays.fill(ADD_ENTITY_HANDLERS, BukkitNetworkManager.Handlers.DO_NOTHING);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$FALLING_BLOCK$registryId] = (user, event) -> { ADD_ENTITY_HANDLERS[MEntityTypes.FALLING_BLOCK$registryId] = (user, event) -> {
FriendlyByteBuf buf = event.getBuffer(); FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt(); int id = buf.readVarInt();
UUID uuid = buf.readUUID(); UUID uuid = buf.readUUID();
@@ -122,28 +121,27 @@ public class PacketConsumers {
} }
}; };
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$BLOCK_DISPLAY$registryId] = simpleAddEntityHandler(BlockDisplayPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.BLOCK_DISPLAY$registryId] = simpleAddEntityHandler(BlockDisplayPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ITEM_DISPLAY$registryId] = simpleAddEntityHandler(ItemDisplayPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.EGG$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$EGG$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.POTION$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$POTION$registryId] = createOptionalCustomProjectileEntityHandler(); ADD_ENTITY_HANDLERS[MEntityTypes.TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler();
if (VersionHelper.isOrAbove1_20_5()) { if (VersionHelper.isOrAbove1_20_5()) {
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[MEntityTypes.OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
} }
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ITEM_DISPLAY$registryId] = (user, event) -> { ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_DISPLAY$registryId] = (user, event) -> {
FriendlyByteBuf buf = event.getBuffer(); FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt(); int id = buf.readVarInt();
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id);
@@ -153,10 +151,12 @@ public class PacketConsumers {
if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { if (Config.hideBaseEntity() && !furniture.hasExternalModel()) {
event.setCancelled(true); event.setCancelled(true);
} }
} else {
user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE);
} }
}; };
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$INTERACTION$registryId] = (user, event) -> { ADD_ENTITY_HANDLERS[MEntityTypes.INTERACTION$registryId] = (user, event) -> {
if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.instance$EntityType$INTERACTION) return; if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.INTERACTION) return;
FriendlyByteBuf buf = event.getBuffer(); FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt(); int id = buf.readVarInt();
// Cancel collider entity packet // Cancel collider entity packet
@@ -166,8 +166,8 @@ public class PacketConsumers {
user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE);
} }
}; };
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$OAK_BOAT$registryId] = (user, event) -> { ADD_ENTITY_HANDLERS[MEntityTypes.OAK_BOAT$registryId] = (user, event) -> {
if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.instance$EntityType$OAK_BOAT) return; if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.OAK_BOAT) return;
FriendlyByteBuf buf = event.getBuffer(); FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt(); int id = buf.readVarInt();
// Cancel collider entity packet // Cancel collider entity packet
@@ -1106,13 +1106,13 @@ public class PacketConsumers {
Object blockPos = FastNMS.INSTANCE.field$ServerboundPlayerActionPacket$pos(packet); Object blockPos = FastNMS.INSTANCE.field$ServerboundPlayerActionPacket$pos(packet);
BlockPos pos = LocationUtils.fromBlockPos(blockPos); BlockPos pos = LocationUtils.fromBlockPos(blockPos);
if (VersionHelper.isFolia()) { if (VersionHelper.isFolia()) {
BukkitCraftEngine.instance().scheduler().sync().run(() -> { platformPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> {
try { try {
handlePlayerActionPacketOnMainThread(player, world, pos, packet); handlePlayerActionPacketOnMainThread(player, world, pos, packet);
} catch (Exception e) { } catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPlayerActionPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundPlayerActionPacket", e);
} }
}, world, pos.x() >> 4, pos.z() >> 4); }, () -> {});
} else { } else {
handlePlayerActionPacketOnMainThread(player, world, pos, packet); handlePlayerActionPacketOnMainThread(player, world, pos, packet);
} }
@@ -1172,21 +1172,21 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HELLO_C2S = (user, event, packet) -> { public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HELLO_C2S = (user, event, packet) -> {
try { try {
BukkitServerPlayer player = (BukkitServerPlayer) user; BukkitServerPlayer player = (BukkitServerPlayer) user;
String name = (String) NetworkReflections.field$ServerboundHelloPacket$name.get(packet); String name = (String) NetworkReflections.methodHandle$ServerboundHelloPacket$nameGetter.invokeExact(packet);
player.setName(name); player.setName(name);
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
UUID uuid = (UUID) NetworkReflections.field$ServerboundHelloPacket$uuid.get(packet); UUID uuid = (UUID) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet);
player.setUUID(uuid); player.setUUID(uuid);
} else { } else {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Optional<UUID> uuid = (Optional<UUID>) NetworkReflections.field$ServerboundHelloPacket$uuid.get(packet); Optional<UUID> uuid = (Optional<UUID>) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet);
if (uuid.isPresent()) { if (uuid.isPresent()) {
player.setUUID(uuid.get()); player.setUUID(uuid.get());
} else { } else {
player.setUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); player.setUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)));
} }
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundHelloPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundHelloPacket", e);
} }
}; };
@@ -1223,10 +1223,10 @@ public class PacketConsumers {
player.clearView(); player.clearView();
Object dimensionKey; Object dimensionKey;
if (!VersionHelper.isOrAbove1_20_2()) { if (!VersionHelper.isOrAbove1_20_2()) {
dimensionKey = NetworkReflections.field$ClientboundRespawnPacket$dimension.get(packet); dimensionKey = NetworkReflections.methodHandle$ClientboundRespawnPacket$dimensionGetter.invokeExact(packet);
} else { } else {
Object commonInfo = NetworkReflections.field$ClientboundRespawnPacket$commonPlayerSpawnInfo.get(packet); Object commonInfo = NetworkReflections.methodHandle$ClientboundRespawnPacket$commonPlayerSpawnInfoGetter.invokeExact(packet);
dimensionKey = NetworkReflections.field$CommonPlayerSpawnInfo$dimension.get(commonInfo); dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo);
} }
Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey);
World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString())));
@@ -1237,7 +1237,7 @@ public class PacketConsumers {
} else { } else {
CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist"); CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist");
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket", e); CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket", e);
} }
}; };
@@ -1251,10 +1251,10 @@ public class PacketConsumers {
if (BukkitNetworkManager.hasViaVersion()) { if (BukkitNetworkManager.hasViaVersion()) {
user.setProtocolVersion(CraftEngine.instance().compatibilityManager().getPlayerProtocolVersion(player.uuid())); user.setProtocolVersion(CraftEngine.instance().compatibilityManager().getPlayerProtocolVersion(player.uuid()));
} }
dimensionKey = NetworkReflections.field$ClientboundLoginPacket$dimension.get(packet); dimensionKey = NetworkReflections.methodHandle$ClientboundLoginPacket$dimensionGetter.invokeExact(packet);
} else { } else {
Object commonInfo = NetworkReflections.field$ClientboundLoginPacket$commonPlayerSpawnInfo.get(packet); Object commonInfo = NetworkReflections.methodHandle$ClientboundLoginPacket$commonPlayerSpawnInfoGetter.invokeExact(packet);
dimensionKey = NetworkReflections.field$CommonPlayerSpawnInfo$dimension.get(commonInfo); dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo);
} }
Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey);
World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString())));
@@ -1265,7 +1265,7 @@ public class PacketConsumers {
} else { } else {
CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket: World " + location + " does not exist"); CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket: World " + location + " does not exist");
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket", e); CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket", e);
} }
}; };
@@ -1282,25 +1282,25 @@ public class PacketConsumers {
player.platformPlayer().getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { player.platformPlayer().getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> {
try { try {
handleSetCreativeSlotPacketOnMainThread(player, packet); handleSetCreativeSlotPacketOnMainThread(player, packet);
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e);
} }
}, () -> {}); }, () -> {});
} else { } else {
handleSetCreativeSlotPacketOnMainThread(player, packet); handleSetCreativeSlotPacketOnMainThread(player, packet);
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e);
} }
}; };
private static void handleSetCreativeSlotPacketOnMainThread(BukkitServerPlayer player, Object packet) throws Exception { private static void handleSetCreativeSlotPacketOnMainThread(BukkitServerPlayer player, Object packet) throws Throwable {
Player bukkitPlayer = player.platformPlayer(); Player bukkitPlayer = player.platformPlayer();
if (bukkitPlayer == null) return; if (bukkitPlayer == null) return;
if (bukkitPlayer.getGameMode() != GameMode.CREATIVE) return; if (bukkitPlayer.getGameMode() != GameMode.CREATIVE) return;
int slot = VersionHelper.isOrAbove1_20_5() ? NetworkReflections.field$ServerboundSetCreativeModeSlotPacket$slotNum.getShort(packet) : NetworkReflections.field$ServerboundSetCreativeModeSlotPacket$slotNum.getInt(packet); int slot = VersionHelper.isOrAbove1_20_5() ? (short) NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter.invokeExact(packet) : (int) NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter.invokeExact(packet);
if (slot < 36 || slot > 44) return; if (slot < 36 || slot > 44) return;
ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(NetworkReflections.field$ServerboundSetCreativeModeSlotPacket$itemStack.get(packet)); ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$itemStackGetter.invokeExact(packet));
if (ItemUtils.isEmpty(item)) return; if (ItemUtils.isEmpty(item)) return;
if (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) { if (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) {
return; return;
@@ -1373,14 +1373,14 @@ public class PacketConsumers {
if (!user.isOnline()) return; if (!user.isOnline()) return;
Player player = (Player) user.platformPlayer(); Player player = (Player) user.platformPlayer();
if (player == null) return; if (player == null) return;
Object pos = NetworkReflections.field$ServerboundPickItemFromBlockPacket$pos.get(packet); Object pos = NetworkReflections.methodHandle$ServerboundPickItemFromBlockPacket$posGetter.invokeExact(packet);
if (VersionHelper.isFolia()) { if (VersionHelper.isFolia()) {
int x = FastNMS.INSTANCE.field$Vec3i$x(pos); int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
int z = FastNMS.INSTANCE.field$Vec3i$z(pos); int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
BukkitCraftEngine.instance().scheduler().sync().run(() -> { BukkitCraftEngine.instance().scheduler().sync().run(() -> {
try { try {
handlePickItemFromBlockPacketOnMainThread(player, pos); handlePickItemFromBlockPacketOnMainThread(player, pos);
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e);
} }
}, player.getWorld(), x >> 4, z >> 4); }, player.getWorld(), x >> 4, z >> 4);
@@ -1388,17 +1388,17 @@ public class PacketConsumers {
BukkitCraftEngine.instance().scheduler().sync().run(() -> { BukkitCraftEngine.instance().scheduler().sync().run(() -> {
try { try {
handlePickItemFromBlockPacketOnMainThread(player, pos); handlePickItemFromBlockPacketOnMainThread(player, pos);
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e);
} }
}); });
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e);
} }
}; };
private static void handlePickItemFromBlockPacketOnMainThread(Player player, Object pos) throws Exception { private static void handlePickItemFromBlockPacketOnMainThread(Player player, Object pos) throws Throwable {
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld());
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos); Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos);
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
@@ -1411,7 +1411,7 @@ public class PacketConsumers {
// 1.21.4+ // 1.21.4+
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> PICK_ITEM_FROM_ENTITY = (user, event, packet) -> { public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> PICK_ITEM_FROM_ENTITY = (user, event, packet) -> {
try { try {
int entityId = (int) NetworkReflections.field$ServerboundPickItemFromEntityPacket$id.get(packet); int entityId = (int) NetworkReflections.methodHandle$ServerboundPickItemFromEntityPacket$idGetter.invokeExact(packet);
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
if (furniture == null) return; if (furniture == null) return;
Player player = (Player) user.platformPlayer(); Player player = (Player) user.platformPlayer();
@@ -1420,7 +1420,7 @@ public class PacketConsumers {
player.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { player.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> {
try { try {
handlePickItemFromEntityOnMainThread(player, furniture); handlePickItemFromEntityOnMainThread(player, furniture);
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e);
} }
}, () -> {}); }, () -> {});
@@ -1428,23 +1428,23 @@ public class PacketConsumers {
BukkitCraftEngine.instance().scheduler().sync().run(() -> { BukkitCraftEngine.instance().scheduler().sync().run(() -> {
try { try {
handlePickItemFromEntityOnMainThread(player, furniture); handlePickItemFromEntityOnMainThread(player, furniture);
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e);
} }
}); });
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e);
} }
}; };
private static void handlePickItemFromEntityOnMainThread(Player player, BukkitFurniture furniture) throws Exception { private static void handlePickItemFromEntityOnMainThread(Player player, BukkitFurniture furniture) throws Throwable {
Key itemId = furniture.config().settings().itemId(); Key itemId = furniture.config().settings().itemId();
if (itemId == null) return; if (itemId == null) return;
pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.baseEntity())); pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.baseEntity()));
} }
private static void pickItem(Player player, Key itemId, @Nullable Object blockPos, @Nullable Object entity) throws IllegalAccessException, InvocationTargetException { private static void pickItem(Player player, Key itemId, @Nullable Object blockPos, @Nullable Object entity) throws Throwable {
ItemStack itemStack = BukkitCraftEngine.instance().itemManager().buildCustomItemStack(itemId, BukkitCraftEngine.instance().adapt(player)); ItemStack itemStack = BukkitCraftEngine.instance().itemManager().buildCustomItemStack(itemId, BukkitCraftEngine.instance().adapt(player));
if (itemStack == null) { if (itemStack == null) {
CraftEngine.instance().logger().warn("Item: " + itemId + " is not a valid item"); CraftEngine.instance().logger().warn("Item: " + itemId + " is not a valid item");
@@ -1453,15 +1453,15 @@ public class PacketConsumers {
assert CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem != null; assert CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem != null;
if (VersionHelper.isOrAbove1_21_5()) { if (VersionHelper.isOrAbove1_21_5()) {
CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke( CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke(
CoreReflections.field$ServerPlayer$connection.get(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), CoreReflections.methodHandle$ServerPlayer$connectionGetter.invokeExact(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)),
FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack), blockPos, entity, true); FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack), blockPos, entity, true);
} else { } else {
CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke( CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke(
CoreReflections.field$ServerPlayer$connection.get(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)); CoreReflections.methodHandle$ServerPlayer$connectionGetter.invokeExact(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack));
} }
} }
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> ADD_ENTITY_BYTEBUFFER = (user, event) -> { public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> ADD_ENTITY = (user, event) -> {
try { try {
FriendlyByteBuf buf = event.getBuffer(); FriendlyByteBuf buf = event.getBuffer();
buf.readVarInt(); buf.readVarInt();
@@ -1509,83 +1509,108 @@ public class PacketConsumers {
} }
}; };
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> INTERACT_ENTITY = (user, event, packet) -> { public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> INTERACT_ENTITY = (user, event) -> {
try { try {
Player player = (Player) user.platformPlayer(); FriendlyByteBuf buf = event.getBuffer();
if (player == null) return; int entityId = BukkitNetworkManager.hasModelEngine() ?
int entityId; CraftEngine.instance().compatibilityManager().interactionToBaseEntity(buf.readVarInt()) :
if (BukkitNetworkManager.hasModelEngine()) { buf.readVarInt();
int fakeId = FastNMS.INSTANCE.field$ServerboundInteractPacket$entityId(packet);
entityId = CraftEngine.instance().compatibilityManager().interactionToBaseEntity(fakeId);
} else {
entityId = FastNMS.INSTANCE.field$ServerboundInteractPacket$entityId(packet);
}
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
if (furniture == null) return; if (furniture == null) return;
Object action = NetworkReflections.field$ServerboundInteractPacket$action.get(packet); int actionType = buf.readVarInt();
Object actionType = NetworkReflections.method$ServerboundInteractPacket$Action$getType.invoke(action);
if (actionType == null) return;
Location location = furniture.baseEntity().getLocation();
BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user;
if (serverPlayer.isSpectatorMode()) return; if (serverPlayer.isSpectatorMode()) return;
BukkitCraftEngine.instance().scheduler().sync().run(() -> { Player platformPlayer = serverPlayer.platformPlayer();
if (actionType == NetworkReflections.instance$ServerboundInteractPacket$ActionType$ATTACK) { Location location = furniture.baseEntity().getLocation();
// todo 冒险模式破坏工具白名单
if (serverPlayer.isAdventureMode()) return; Runnable mainThreadTask;
if (furniture.isValid()) { if (actionType == 1) {
if (!BukkitCraftEngine.instance().antiGrief().canBreak(player, location)) { // ATTACK
return; boolean usingSecondaryAction = buf.readBoolean();
} if (entityId != furniture.baseEntityId()) {
FurnitureBreakEvent breakEvent = new FurnitureBreakEvent(serverPlayer.platformPlayer(), furniture); event.setChanged(true);
if (EventUtils.fireAndCheckCancel(breakEvent)) { buf.clear();
return; buf.writeVarInt(event.packetID());
buf.writeVarInt(furniture.baseEntityId());
buf.writeVarInt(actionType);
buf.writeBoolean(usingSecondaryAction);
} }
mainThreadTask = () -> {
// todo 冒险模式破坏工具白名单
if (serverPlayer.isAdventureMode() ||
!furniture.isValid() ||
!BukkitCraftEngine.instance().antiGrief().canBreak(platformPlayer, location)
) return;
FurnitureBreakEvent breakEvent = new FurnitureBreakEvent(serverPlayer.platformPlayer(), furniture);
if (EventUtils.fireAndCheckCancel(breakEvent))
return;
Cancellable cancellable = Cancellable.of(breakEvent::isCancelled, breakEvent::setCancelled);
// execute functions // execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, furniture) .withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.HAND, InteractionHand.MAIN_HAND)
.withParameter(DirectContextParameters.ITEM_IN_HAND, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND))
.withParameter(DirectContextParameters.POSITION, furniture.position()) .withParameter(DirectContextParameters.POSITION, furniture.position())
); );
furniture.config().execute(context, EventTrigger.LEFT_CLICK); furniture.config().execute(context, EventTrigger.LEFT_CLICK);
furniture.config().execute(context, EventTrigger.BREAK); furniture.config().execute(context, EventTrigger.BREAK);
if (cancellable.isCancelled()) {
return;
}
CraftEngineFurniture.remove(furniture, serverPlayer, !serverPlayer.isCreativeMode(), true); CraftEngineFurniture.remove(furniture, serverPlayer, !serverPlayer.isCreativeMode(), true);
};
} else if (actionType == 2) {
// INTERACT_AT
float x = buf.readFloat();
float y = buf.readFloat();
float z = buf.readFloat();
Location interactionPoint = new Location(platformPlayer.getWorld(), x, y, z);
InteractionHand hand = buf.readVarInt() == 0 ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
boolean usingSecondaryAction = buf.readBoolean();
if (entityId != furniture.baseEntityId()) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(furniture.baseEntityId());
buf.writeVarInt(actionType);
buf.writeFloat(x).writeFloat(y).writeFloat(z);
buf.writeVarInt(hand == InteractionHand.MAIN_HAND ? 0 : 1);
buf.writeBoolean(usingSecondaryAction);
} }
} else if (actionType == NetworkReflections.instance$ServerboundInteractPacket$ActionType$INTERACT_AT) {
InteractionHand hand;
Location interactionPoint;
try {
Object interactionHand = NetworkReflections.field$ServerboundInteractPacket$InteractionAtLocationAction$hand.get(action);
hand = interactionHand == CoreReflections.instance$InteractionHand$MAIN_HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
Object vec3 = NetworkReflections.field$ServerboundInteractPacket$InteractionAtLocationAction$location.get(action);
double x = FastNMS.INSTANCE.field$Vec3$x(vec3); mainThreadTask = () -> {
double y = FastNMS.INSTANCE.field$Vec3$y(vec3);
double z = FastNMS.INSTANCE.field$Vec3$z(vec3);
interactionPoint = new Location(location.getWorld(), x, y, z);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to get interaction hand from interact packet", e);
}
FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint); FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint);
if (EventUtils.fireAndCheckCancel(interactEvent)) { if (EventUtils.fireAndCheckCancel(interactEvent)) {
return; return;
} }
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
Cancellable cancellable = Cancellable.of(interactEvent::isCancelled, interactEvent::setCancelled);
// execute functions // execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.FURNITURE, furniture) .withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.HAND, hand)
.withParameter(DirectContextParameters.POSITION, furniture.position()) .withParameter(DirectContextParameters.POSITION, furniture.position())
); );
furniture.config().execute(context, EventTrigger.RIGHT_CLICK); furniture.config().execute(context, EventTrigger.RIGHT_CLICK);
if (cancellable.isCancelled()) {
return;
}
if (player.isSneaking()) { // 必须从网络包层面处理,否则无法获取交互的具体实体
if (serverPlayer.isSecondaryUseActive() && itemInHand != null) {
// try placing another furniture above it // try placing another furniture above it
AABB hitBox = furniture.aabbByEntityId(entityId); AABB hitBox = furniture.aabbByEntityId(entityId);
if (hitBox == null) return; if (hitBox == null) return;
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
if (itemInHand == null) return; Location eyeLocation = platformPlayer.getEyeLocation();
Optional<CustomItem<ItemStack>> optionalCustomitem = itemInHand.getCustomItem();
Location eyeLocation = player.getEyeLocation();
Vector direction = eyeLocation.getDirection(); Vector direction = eyeLocation.getDirection();
Location endLocation = eyeLocation.clone(); Location endLocation = eyeLocation.clone();
endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange()));
@@ -1594,8 +1619,8 @@ public class PacketConsumers {
return; return;
} }
EntityHitResult hitResult = result.get(); EntityHitResult hitResult = result.get();
if (optionalCustomitem.isPresent() && !optionalCustomitem.get().behaviors().isEmpty()) { if (optionalCustomItem.isPresent() && !optionalCustomItem.get().behaviors().isEmpty()) {
for (ItemBehavior behavior : optionalCustomitem.get().behaviors()) { for (ItemBehavior behavior : optionalCustomItem.get().behaviors()) {
if (behavior instanceof FurnitureItemBehavior) { if (behavior instanceof FurnitureItemBehavior) {
behavior.useOnBlock(new UseOnContext(serverPlayer, InteractionHand.MAIN_HAND, new BlockHitResult(hitResult.hitLocation(), hitResult.direction(), BlockPos.fromVec3d(hitResult.hitLocation()), false))); behavior.useOnBlock(new UseOnContext(serverPlayer, InteractionHand.MAIN_HAND, new BlockHitResult(hitResult.hitLocation(), hitResult.direction(), BlockPos.fromVec3d(hitResult.hitLocation()), false)));
return; return;
@@ -1604,7 +1629,12 @@ public class PacketConsumers {
} }
// now simulate vanilla item behavior // now simulate vanilla item behavior
serverPlayer.setResendSound(); serverPlayer.setResendSound();
FastNMS.INSTANCE.simulateInteraction(serverPlayer.serverPlayer(), DirectionUtils.toNMSDirection(hitResult.direction()), hitResult.hitLocation().x, hitResult.hitLocation().y, hitResult.hitLocation().z, LocationUtils.toBlockPos(hitResult.blockPos())); FastNMS.INSTANCE.simulateInteraction(
serverPlayer.serverPlayer(),
DirectionUtils.toNMSDirection(hitResult.direction()),
hitResult.hitLocation().x, hitResult.hitLocation().y, hitResult.hitLocation().z,
LocationUtils.toBlockPos(hitResult.blockPos())
);
} else { } else {
furniture.findFirstAvailableSeat(entityId).ifPresent(seatPos -> { furniture.findFirstAvailableSeat(entityId).ifPresent(seatPos -> {
if (furniture.tryOccupySeat(seatPos)) { if (furniture.tryOccupySeat(seatPos)) {
@@ -1612,9 +1642,30 @@ public class PacketConsumers {
} }
}); });
} }
};
} else if (actionType == 0) {
int hand = buf.readVarInt();
boolean usingSecondaryAction = buf.readBoolean();
if (entityId != furniture.baseEntityId()) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(furniture.baseEntityId());
buf.writeVarInt(actionType);
buf.writeVarInt(hand);
buf.writeBoolean(usingSecondaryAction);
} }
}, player.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); return;
} catch (Exception e) { } else {
return;
}
if (VersionHelper.isFolia()) {
platformPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), t -> mainThreadTask.run(), () -> {});
} else {
BukkitCraftEngine.instance().scheduler().executeSync(mainThreadTask);
}
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundInteractPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundInteractPacket", e);
} }
}; };
@@ -1699,20 +1750,20 @@ public class PacketConsumers {
if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) {
return; return;
} }
String message = (String) NetworkReflections.field$ServerboundRenameItemPacket$name.get(packet); String message = (String) NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameGetter.invokeExact(packet);
if (message != null && !message.isEmpty()) { if (message != null && !message.isEmpty()) {
// check bypass // check bypass
FontManager manager = CraftEngine.instance().fontManager(); FontManager manager = CraftEngine.instance().fontManager();
IllegalCharacterProcessResult result = manager.processIllegalCharacters(message); IllegalCharacterProcessResult result = manager.processIllegalCharacters(message);
if (result.has()) { if (result.has()) {
try { try {
NetworkReflections.field$ServerboundRenameItemPacket$name.set(packet, result.text()); NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameSetter.invokeExact(packet, result.text());
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to replace chat", e); CraftEngine.instance().logger().warn("Failed to replace chat", e);
} }
} }
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e);
} }
}; };
@@ -1725,7 +1776,7 @@ public class PacketConsumers {
if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) { if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) {
return; return;
} }
String[] lines = (String[]) NetworkReflections.field$ServerboundSignUpdatePacket$lines.get(packet); String[] lines = (String[]) NetworkReflections.methodHandle$ServerboundSignUpdatePacket$linesGetter.invokeExact(packet);
FontManager manager = CraftEngine.instance().fontManager(); FontManager manager = CraftEngine.instance().fontManager();
if (!manager.isDefaultFontInUse()) return; if (!manager.isDefaultFontInUse()) return;
for (int i = 0; i < lines.length; i++) { for (int i = 0; i < lines.length; i++) {
@@ -1737,7 +1788,7 @@ public class PacketConsumers {
} }
} }
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundSignUpdatePacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundSignUpdatePacket", e);
} }
}; };
@@ -1756,9 +1807,9 @@ public class PacketConsumers {
boolean changed = false; boolean changed = false;
List<String> pages = (List<String>) NetworkReflections.field$ServerboundEditBookPacket$pages.get(packet); List<String> pages = (List<String>) NetworkReflections.methodHandle$ServerboundEditBookPacket$pagesGetter.invokeExact(packet);
List<String> newPages = new ArrayList<>(pages.size()); List<String> newPages = new ArrayList<>(pages.size());
Optional<String> title = (Optional<String>) NetworkReflections.field$ServerboundEditBookPacket$title.get(packet); Optional<String> title = (Optional<String>) NetworkReflections.methodHandle$ServerboundEditBookPacket$titleGetter.invokeExact(packet);
Optional<String> newTitle; Optional<String> newTitle;
if (title.isPresent()) { if (title.isPresent()) {
@@ -1782,13 +1833,13 @@ public class PacketConsumers {
if (changed) { if (changed) {
Object newPacket = NetworkReflections.constructor$ServerboundEditBookPacket.newInstance( Object newPacket = NetworkReflections.constructor$ServerboundEditBookPacket.newInstance(
NetworkReflections.field$ServerboundEditBookPacket$slot.get(packet), (int) NetworkReflections.methodHandle$ServerboundEditBookPacket$slotGetter.invokeExact(packet),
newPages, newPages,
newTitle newTitle
); );
event.replacePacket(newPacket); event.replacePacket(newPacket);
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundEditBookPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundEditBookPacket", e);
} }
}; };
@@ -1815,7 +1866,7 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CUSTOM_PAYLOAD = (user, event, packet) -> { public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CUSTOM_PAYLOAD = (user, event, packet) -> {
try { try {
if (!VersionHelper.isOrAbove1_20_5()) return; if (!VersionHelper.isOrAbove1_20_5()) return;
Object payload = NetworkReflections.field$ServerboundCustomPayloadPacket$payload.get(packet); Object payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet);
if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) {
Payload discardedPayload = DiscardedPayload.from(payload); Payload discardedPayload = DiscardedPayload.from(payload);
if (discardedPayload == null || !discardedPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) if (discardedPayload == null || !discardedPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY))
@@ -1844,7 +1895,7 @@ public class PacketConsumers {
} }
} }
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e);
} }
}; };
@@ -2234,9 +2285,9 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HANDSHAKE_C2S = (user, event, packet) -> { public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HANDSHAKE_C2S = (user, event, packet) -> {
try { try {
if (BukkitNetworkManager.hasViaVersion()) return; if (BukkitNetworkManager.hasViaVersion()) return;
int protocolVersion = NetworkReflections.field$ClientIntentionPacket$protocolVersion.getInt(packet); int protocolVersion = (int) NetworkReflections.methodHandle$ClientIntentionPacket$protocolVersionGetter.invokeExact(packet);
user.setProtocolVersion(protocolVersion); user.setProtocolVersion(protocolVersion);
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ClientIntentionPacket", e); CraftEngine.instance().logger().warn("Failed to handle ClientIntentionPacket", e);
} }
}; };
@@ -2254,7 +2305,7 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> RESOURCE_PACK_RESPONSE = (user, event, packet) -> { public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> RESOURCE_PACK_RESPONSE = (user, event, packet) -> {
try { try {
if (user.sentResourcePack() || !Config.sendPackOnJoin() || !Config.kickOnDeclined()) return; if (user.sentResourcePack() || !Config.sendPackOnJoin() || !Config.kickOnDeclined()) return;
Object action = NetworkReflections.field$ServerboundResourcePackPacket$action.get(packet); Object action = NetworkReflections.methodHandle$ServerboundResourcePackPacket$actionGetter.invokeExact(packet);
if (action == null) return; if (action == null) return;
if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$DECLINED if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$DECLINED
|| action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD) { || action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD) {
@@ -2267,7 +2318,7 @@ public class PacketConsumers {
if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$SUCCESSFULLY_LOADED) { if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$SUCCESSFULLY_LOADED) {
user.setSentResourcePack(true); user.setSentResourcePack(true);
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundResourcePackPacket", e); CraftEngine.instance().logger().warn("Failed to handle ServerboundResourcePackPacket", e);
} }
}; };
@@ -2276,28 +2327,17 @@ public class PacketConsumers {
try { try {
Object player = user.serverPlayer(); Object player = user.serverPlayer();
if (player == null) return; if (player == null) return;
int entityId = NetworkReflections.field$ClientboundEntityEventPacket$entityId.getInt(packet); int entityId = (int) NetworkReflections.methodHandle$ClientboundEntityEventPacket$entityIdGetter.invokeExact(packet);
if (entityId != FastNMS.INSTANCE.method$Entity$getId(player)) return; if (entityId != FastNMS.INSTANCE.method$Entity$getId(player)) return;
byte eventId = NetworkReflections.field$ClientboundEntityEventPacket$eventId.getByte(packet); byte eventId = (byte) NetworkReflections.methodHandle$ClientboundEntityEventPacket$eventIdGetter.invokeExact(packet);
if (eventId >= 24 && eventId <= 28) { if (eventId >= 24 && eventId <= 28) {
CraftEngine.instance().fontManager().refreshEmojiSuggestions(user.uuid()); CraftEngine.instance().fontManager().refreshEmojiSuggestions(user.uuid());
} }
} catch (Exception e) { } catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e); CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e);
} }
}; };
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_ENTITY = (user, event, packet) -> {
try {
int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> { public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> {
try { try {
int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet);
@@ -2310,4 +2350,15 @@ public class PacketConsumers {
} }
}; };
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_ENTITY = (user, event, packet) -> {
try {
int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet);
EntityPacketHandler handler = user.entityPacketHandlers().get(entityId);
if (handler != null) {
handler.handleMove(user, event, packet);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket", e);
}
};
} }

View File

@@ -52,7 +52,11 @@ public interface PacketIds {
int clientboundSetPlayerInventoryPacket(); int clientboundSetPlayerInventoryPacket();
int clientboundBlockEventPacket();
int serverboundContainerClickPacket(); int serverboundContainerClickPacket();
int serverboundSetCreativeModeSlotPacket(); int serverboundSetCreativeModeSlotPacket();
int serverboundInteractPacket();
} }

View File

@@ -2,8 +2,10 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils; import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
@@ -27,6 +29,11 @@ public class CommonItemPacketHandler implements EntityPacketHandler {
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.ITEM_DATA_ID) { if (entityDataId == EntityDataUtils.ITEM_DATA_ID) {
Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
// TODO 检查为什么会导致问题难道是其他插件乱发entity id
if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) {
CraftEngine.instance().logger().warn("Invalid item data for entity " + id);
continue;
}
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack);
Optional<ItemStack> optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user); Optional<ItemStack> optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user);
if (optional.isPresent()) { if (optional.isPresent()) {

View File

@@ -11,4 +11,9 @@ public class FurnitureCollisionPacketHandler implements EntityPacketHandler {
public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) {
event.setCancelled(true); event.setCancelled(true);
} }
@Override
public void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) {
event.setCancelled(true);
}
} }

View File

@@ -24,4 +24,9 @@ public class FurniturePacketHandler implements EntityPacketHandler {
public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) {
event.setCancelled(true); event.setCancelled(true);
} }
@Override
public void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) {
event.setCancelled(true);
}
} }

View File

@@ -81,7 +81,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler {
buf.writeVarInt(event.packetID()); buf.writeVarInt(event.packetID());
buf.writeVarInt(this.entityId); buf.writeVarInt(this.entityId);
buf.writeUUID(uuid); buf.writeUUID(uuid);
buf.writeVarInt(MEntityTypes.instance$EntityType$ITEM_DISPLAY$registryId); buf.writeVarInt(MEntityTypes.ITEM_DISPLAY$registryId);
buf.writeDouble(x); buf.writeDouble(x);
buf.writeDouble(y); buf.writeDouble(y);
buf.writeDouble(z); buf.writeDouble(z);

View File

@@ -139,4 +139,14 @@ public class PacketIds1_20 implements PacketIds {
public int serverboundSetCreativeModeSlotPacket() { public int serverboundSetCreativeModeSlotPacket() {
return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket); return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket);
} }
@Override
public int clientboundBlockEventPacket() {
return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundBlockEventPacket);
}
@Override
public int serverboundInteractPacket() {
return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundInteractPacket);
}
} }

View File

@@ -129,6 +129,11 @@ public class PacketIds1_20_5 implements PacketIds {
return PacketIdFinder.clientboundByName("minecraft:set_player_inventory"); return PacketIdFinder.clientboundByName("minecraft:set_player_inventory");
} }
@Override
public int clientboundBlockEventPacket() {
return PacketIdFinder.clientboundByName("minecraft:block_event");
}
@Override @Override
public int serverboundContainerClickPacket() { public int serverboundContainerClickPacket() {
return PacketIdFinder.serverboundByName("minecraft:container_click"); return PacketIdFinder.serverboundByName("minecraft:container_click");
@@ -138,4 +143,9 @@ public class PacketIds1_20_5 implements PacketIds {
public int serverboundSetCreativeModeSlotPacket() { public int serverboundSetCreativeModeSlotPacket() {
return PacketIdFinder.serverboundByName("minecraft:set_creative_mode_slot"); return PacketIdFinder.serverboundByName("minecraft:set_creative_mode_slot");
} }
@Override
public int serverboundInteractPacket() {
return PacketIdFinder.serverboundByName("minecraft:interact");
}
} }

View File

@@ -5,12 +5,15 @@ import com.google.gson.JsonElement;
import com.mojang.serialization.DynamicOps; import com.mojang.serialization.DynamicOps;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException;
import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils; import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils;
import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.*; import java.util.*;
@@ -1353,10 +1356,6 @@ public final class CoreReflections {
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getBlockSupportShape", "b_"}, clazz$BlockState, clazz$BlockGetter, CoreReflections.clazz$BlockPos) ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getBlockSupportShape", "b_"}, clazz$BlockState, clazz$BlockGetter, CoreReflections.clazz$BlockPos)
); );
public static final Method method$LevelAccessor$scheduleTick = requireNonNull(
ReflectionUtils.getMethod(clazz$LevelAccessor, void.class, CoreReflections.clazz$BlockPos, clazz$Block, int.class)
);
public static final Field field$BlockBehaviour$properties = requireNonNull( public static final Field field$BlockBehaviour$properties = requireNonNull(
ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, clazz$BlockBehaviour$Properties, 0) ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, clazz$BlockBehaviour$Properties, 0)
); );
@@ -3265,4 +3264,48 @@ public final class CoreReflections {
public static final Method method$Registry$asLookup = ReflectionUtils.getMethod( public static final Method method$Registry$asLookup = ReflectionUtils.getMethod(
clazz$Registry, clazz$HolderLookup$RegistryLookup, new String[]{"asLookup", "p"} clazz$Registry, clazz$HolderLookup$RegistryLookup, new String[]{"asLookup", "p"}
); );
public static final Field field$ServerEntity$broadcast = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerEntity, Consumer.class, 0
)
);
public static final MethodHandle methodHandle$ServerEntity$broadcastSetter;
public static final MethodHandle methodHandle$ServerEntity$updateIntervalSetter;
public static final MethodHandle methodHandle$ServerPlayer$connectionGetter;
static {
try {
methodHandle$ServerEntity$broadcastSetter = requireNonNull(
ReflectionUtils.unreflectSetter(field$ServerEntity$broadcast)
.asType(MethodType.methodType(void.class, Object.class, Consumer.class))
);
methodHandle$ServerEntity$updateIntervalSetter = requireNonNull(
ReflectionUtils.unreflectSetter(field$ServerEntity$updateInterval)
.asType(MethodType.methodType(void.class, Object.class, int.class))
);
methodHandle$ServerPlayer$connectionGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerPlayer$connection)
.asType(MethodType.methodType(Object.class, Object.class))
);
} catch (IllegalAccessException e) {
throw new ReflectionInitException("Failed to initialize reflection", e);
}
}
public static final Class<?> clazz$BaseFireBlock = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.BlockFireAbstract",
"world.level.block.BaseFireBlock"
)
);
public static final Method method$BaseFireBlock$canBePlacedAt = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$BaseFireBlock, boolean.class, clazz$Level, clazz$BlockPos, clazz$Direction)
);
public static final Field field$FireBlock$igniteOdds = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$FireBlock, Object2IntMap.class, 0)
);
} }

View File

@@ -16,6 +16,8 @@ public final class MBlocks {
public static final Object ICE; public static final Object ICE;
public static final Object SHORT_GRASS; public static final Object SHORT_GRASS;
public static final Object SHORT_GRASS$defaultState; public static final Object SHORT_GRASS$defaultState;
public static final Object SHULKER_BOX;
public static final Object COMPOSTER;
private static Object getById(String id) throws ReflectiveOperationException { private static Object getById(String id) throws ReflectiveOperationException {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -33,6 +35,8 @@ public final class MBlocks {
ICE = getById("ice"); ICE = getById("ice");
SHORT_GRASS = getById(VersionHelper.isOrAbove1_20_3() ? "short_grass" : "grass"); SHORT_GRASS = getById(VersionHelper.isOrAbove1_20_3() ? "short_grass" : "grass");
SHORT_GRASS$defaultState = CoreReflections.method$Block$defaultBlockState.invoke(SHORT_GRASS); SHORT_GRASS$defaultState = CoreReflections.method$Block$defaultBlockState.invoke(SHORT_GRASS);
SHULKER_BOX = getById("shulker_box");
COMPOSTER = getById("composter");
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init Blocks", e); throw new ReflectionInitException("Failed to init Blocks", e);
} }

View File

@@ -7,50 +7,50 @@ import net.momirealms.craftengine.core.util.VersionHelper;
public final class MEntityTypes { public final class MEntityTypes {
private MEntityTypes() {} private MEntityTypes() {}
public static final Object instance$EntityType$TEXT_DISPLAY; public static final Object TEXT_DISPLAY;
public static final int instance$EntityType$TEXT_DISPLAY$registryId; public static final int TEXT_DISPLAY$registryId;
public static final Object instance$EntityType$ITEM_DISPLAY; public static final Object ITEM_DISPLAY;
public static final int instance$EntityType$ITEM_DISPLAY$registryId; public static final int ITEM_DISPLAY$registryId;
public static final Object instance$EntityType$BLOCK_DISPLAY; public static final Object BLOCK_DISPLAY;
public static final int instance$EntityType$BLOCK_DISPLAY$registryId; public static final int BLOCK_DISPLAY$registryId;
public static final Object instance$EntityType$ARMOR_STAND; public static final Object ARMOR_STAND;
public static final int instance$EntityType$ARMOR_STAND$registryId; public static final int ARMOR_STAND$registryId;
public static final Object instance$EntityType$FALLING_BLOCK; public static final Object FALLING_BLOCK;
public static final int instance$EntityType$FALLING_BLOCK$registryId; public static final int FALLING_BLOCK$registryId;
public static final Object instance$EntityType$INTERACTION; public static final Object INTERACTION;
public static final int instance$EntityType$INTERACTION$registryId; public static final int INTERACTION$registryId;
public static final Object instance$EntityType$SHULKER; public static final Object SHULKER;
public static final int instance$EntityType$SHULKER$registryId; public static final int SHULKER$registryId;
public static final Object instance$EntityType$OAK_BOAT; public static final Object OAK_BOAT;
public static final int instance$EntityType$OAK_BOAT$registryId; public static final int OAK_BOAT$registryId;
public static final Object instance$EntityType$TRIDENT; public static final Object TRIDENT;
public static final int instance$EntityType$TRIDENT$registryId; public static final int TRIDENT$registryId;
public static final Object instance$EntityType$SNOWBALL; public static final Object SNOWBALL;
public static final int instance$EntityType$SNOWBALL$registryId; public static final int SNOWBALL$registryId;
public static final Object instance$EntityType$FIREBALL; public static final Object FIREBALL;
public static final int instance$EntityType$FIREBALL$registryId; public static final int FIREBALL$registryId;
public static final Object instance$EntityType$EYE_OF_ENDER; public static final Object EYE_OF_ENDER;
public static final int instance$EntityType$EYE_OF_ENDER$registryId; public static final int EYE_OF_ENDER$registryId;
public static final Object instance$EntityType$FIREWORK_ROCKET; public static final Object FIREWORK_ROCKET;
public static final int instance$EntityType$FIREWORK_ROCKET$registryId; public static final int FIREWORK_ROCKET$registryId;
public static final Object instance$EntityType$ITEM; public static final Object ITEM;
public static final int instance$EntityType$ITEM$registryId; public static final int ITEM$registryId;
public static final Object instance$EntityType$ITEM_FRAME; public static final Object ITEM_FRAME;
public static final int instance$EntityType$ITEM_FRAME$registryId; public static final int ITEM_FRAME$registryId;
public static final Object instance$EntityType$GLOW_ITEM_FRAME; public static final Object GLOW_ITEM_FRAME;
public static final int instance$EntityType$GLOW_ITEM_FRAME$registryId; public static final int GLOW_ITEM_FRAME$registryId;
public static final Object instance$EntityType$OMINOUS_ITEM_SPAWNER; public static final Object OMINOUS_ITEM_SPAWNER;
public static final int instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId; public static final int OMINOUS_ITEM_SPAWNER$registryId;
public static final Object instance$EntityType$SMALL_FIREBALL; public static final Object SMALL_FIREBALL;
public static final int instance$EntityType$SMALL_FIREBALL$registryId; public static final int SMALL_FIREBALL$registryId;
public static final Object instance$EntityType$EGG; public static final Object EGG;
public static final int instance$EntityType$EGG$registryId; public static final int EGG$registryId;
public static final Object instance$EntityType$ENDER_PEARL; public static final Object ENDER_PEARL;
public static final int instance$EntityType$ENDER_PEARL$registryId; public static final int ENDER_PEARL$registryId;
public static final Object instance$EntityType$EXPERIENCE_BOTTLE; public static final Object EXPERIENCE_BOTTLE;
public static final int instance$EntityType$EXPERIENCE_BOTTLE$registryId; public static final int EXPERIENCE_BOTTLE$registryId;
public static final Object instance$EntityType$POTION; public static final Object POTION;
public static final int instance$EntityType$POTION$registryId; public static final int POTION$registryId;
private static Object getById(String id) throws ReflectiveOperationException { private static Object getById(String id) throws ReflectiveOperationException {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -64,50 +64,50 @@ public final class MEntityTypes {
static { static {
try { try {
instance$EntityType$TEXT_DISPLAY = getById("text_display"); TEXT_DISPLAY = getById("text_display");
instance$EntityType$TEXT_DISPLAY$registryId = getRegistryId(instance$EntityType$TEXT_DISPLAY); TEXT_DISPLAY$registryId = getRegistryId(TEXT_DISPLAY);
instance$EntityType$ITEM_DISPLAY = getById("item_display"); ITEM_DISPLAY = getById("item_display");
instance$EntityType$ITEM_DISPLAY$registryId = getRegistryId(instance$EntityType$ITEM_DISPLAY); ITEM_DISPLAY$registryId = getRegistryId(ITEM_DISPLAY);
instance$EntityType$BLOCK_DISPLAY = getById("block_display"); BLOCK_DISPLAY = getById("block_display");
instance$EntityType$BLOCK_DISPLAY$registryId = getRegistryId(instance$EntityType$BLOCK_DISPLAY); BLOCK_DISPLAY$registryId = getRegistryId(BLOCK_DISPLAY);
instance$EntityType$FALLING_BLOCK = getById("falling_block"); FALLING_BLOCK = getById("falling_block");
instance$EntityType$FALLING_BLOCK$registryId = getRegistryId(instance$EntityType$FALLING_BLOCK); FALLING_BLOCK$registryId = getRegistryId(FALLING_BLOCK);
instance$EntityType$INTERACTION = getById("interaction"); INTERACTION = getById("interaction");
instance$EntityType$INTERACTION$registryId = getRegistryId(instance$EntityType$INTERACTION); INTERACTION$registryId = getRegistryId(INTERACTION);
instance$EntityType$SHULKER = getById("shulker"); SHULKER = getById("shulker");
instance$EntityType$SHULKER$registryId = getRegistryId(instance$EntityType$SHULKER); SHULKER$registryId = getRegistryId(SHULKER);
instance$EntityType$ARMOR_STAND = getById("armor_stand"); ARMOR_STAND = getById("armor_stand");
instance$EntityType$ARMOR_STAND$registryId = getRegistryId(instance$EntityType$ARMOR_STAND); ARMOR_STAND$registryId = getRegistryId(ARMOR_STAND);
instance$EntityType$OAK_BOAT = getById(VersionHelper.isOrAbove1_21_2() ? "oak_boat" : "boat"); OAK_BOAT = getById(VersionHelper.isOrAbove1_21_2() ? "oak_boat" : "boat");
instance$EntityType$OAK_BOAT$registryId = getRegistryId(instance$EntityType$OAK_BOAT); OAK_BOAT$registryId = getRegistryId(OAK_BOAT);
instance$EntityType$TRIDENT = getById("trident"); TRIDENT = getById("trident");
instance$EntityType$TRIDENT$registryId = getRegistryId(instance$EntityType$TRIDENT); TRIDENT$registryId = getRegistryId(TRIDENT);
instance$EntityType$SNOWBALL = getById("snowball"); SNOWBALL = getById("snowball");
instance$EntityType$SNOWBALL$registryId = getRegistryId(instance$EntityType$SNOWBALL); SNOWBALL$registryId = getRegistryId(SNOWBALL);
instance$EntityType$FIREBALL = getById("fireball"); FIREBALL = getById("fireball");
instance$EntityType$FIREBALL$registryId = getRegistryId(instance$EntityType$FIREBALL); FIREBALL$registryId = getRegistryId(FIREBALL);
instance$EntityType$EYE_OF_ENDER = getById("eye_of_ender"); EYE_OF_ENDER = getById("eye_of_ender");
instance$EntityType$EYE_OF_ENDER$registryId = getRegistryId(instance$EntityType$EYE_OF_ENDER); EYE_OF_ENDER$registryId = getRegistryId(EYE_OF_ENDER);
instance$EntityType$FIREWORK_ROCKET = getById("firework_rocket"); FIREWORK_ROCKET = getById("firework_rocket");
instance$EntityType$FIREWORK_ROCKET$registryId = getRegistryId(instance$EntityType$FIREWORK_ROCKET); FIREWORK_ROCKET$registryId = getRegistryId(FIREWORK_ROCKET);
instance$EntityType$ITEM = getById("item"); ITEM = getById("item");
instance$EntityType$ITEM$registryId = getRegistryId(instance$EntityType$ITEM); ITEM$registryId = getRegistryId(ITEM);
instance$EntityType$ITEM_FRAME = getById("item_frame"); ITEM_FRAME = getById("item_frame");
instance$EntityType$ITEM_FRAME$registryId = getRegistryId(instance$EntityType$ITEM_FRAME); ITEM_FRAME$registryId = getRegistryId(ITEM_FRAME);
instance$EntityType$GLOW_ITEM_FRAME = getById("glow_item_frame"); GLOW_ITEM_FRAME = getById("glow_item_frame");
instance$EntityType$GLOW_ITEM_FRAME$registryId = getRegistryId(instance$EntityType$GLOW_ITEM_FRAME); GLOW_ITEM_FRAME$registryId = getRegistryId(GLOW_ITEM_FRAME);
instance$EntityType$SMALL_FIREBALL = getById("small_fireball"); SMALL_FIREBALL = getById("small_fireball");
instance$EntityType$SMALL_FIREBALL$registryId = getRegistryId(instance$EntityType$SMALL_FIREBALL); SMALL_FIREBALL$registryId = getRegistryId(SMALL_FIREBALL);
instance$EntityType$EGG = getById("egg"); EGG = getById("egg");
instance$EntityType$EGG$registryId = getRegistryId(instance$EntityType$EGG); EGG$registryId = getRegistryId(EGG);
instance$EntityType$ENDER_PEARL = getById("ender_pearl"); ENDER_PEARL = getById("ender_pearl");
instance$EntityType$ENDER_PEARL$registryId = getRegistryId(instance$EntityType$ENDER_PEARL); ENDER_PEARL$registryId = getRegistryId(ENDER_PEARL);
instance$EntityType$EXPERIENCE_BOTTLE = getById("experience_bottle"); EXPERIENCE_BOTTLE = getById("experience_bottle");
instance$EntityType$EXPERIENCE_BOTTLE$registryId = getRegistryId(instance$EntityType$EXPERIENCE_BOTTLE); EXPERIENCE_BOTTLE$registryId = getRegistryId(EXPERIENCE_BOTTLE);
instance$EntityType$POTION = getById("potion"); POTION = getById("potion");
instance$EntityType$POTION$registryId = getRegistryId(instance$EntityType$POTION); POTION$registryId = getRegistryId(POTION);
instance$EntityType$OMINOUS_ITEM_SPAWNER = VersionHelper.isOrAbove1_20_5() ? getById("ominous_item_spawner") : null; OMINOUS_ITEM_SPAWNER = VersionHelper.isOrAbove1_20_5() ? getById("ominous_item_spawner") : null;
instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId = getRegistryId(instance$EntityType$OMINOUS_ITEM_SPAWNER); OMINOUS_ITEM_SPAWNER$registryId = getRegistryId(OMINOUS_ITEM_SPAWNER);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init EntityTypes", e); throw new ReflectionInitException("Failed to init EntityTypes", e);
} }

View File

@@ -10,21 +10,23 @@ import java.lang.reflect.Type;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
public final class MRegistries { public final class MRegistries {
public static final Object instance$Registries$BLOCK; public static final Object BLOCK;
public static final Object instance$Registries$ITEM; public static final Object ITEM;
public static final Object instance$Registries$ATTRIBUTE; public static final Object ATTRIBUTE;
public static final Object instance$Registries$BIOME; public static final Object BIOME;
public static final Object instance$Registries$MOB_EFFECT; public static final Object MOB_EFFECT;
public static final Object instance$Registries$SOUND_EVENT; public static final Object SOUND_EVENT;
public static final Object instance$Registries$PARTICLE_TYPE; public static final Object PARTICLE_TYPE;
public static final Object instance$Registries$ENTITY_TYPE; public static final Object ENTITY_TYPE;
public static final Object instance$Registries$FLUID; public static final Object FLUID;
public static final Object instance$Registries$RECIPE_TYPE; public static final Object RECIPE_TYPE;
public static final Object instance$Registries$DIMENSION_TYPE; public static final Object DIMENSION_TYPE;
public static final Object instance$Registries$CONFIGURED_FEATURE; public static final Object CONFIGURED_FEATURE;
public static final Object instance$Registries$PLACED_FEATURE; public static final Object PLACED_FEATURE;
@Nullable // 1.21+ @Nullable // 1.21+
public static final Object instance$Registries$JUKEBOX_SONG; public static final Object JUKEBOX_SONG;
@Nullable // 1.21+
public static final Object RECIPE;
static { static {
Field[] fields = CoreReflections.clazz$Registries.getDeclaredFields(); Field[] fields = CoreReflections.clazz$Registries.getDeclaredFields();
@@ -43,6 +45,7 @@ public final class MRegistries {
Object registries$ConfiguredFeature = null; Object registries$ConfiguredFeature = null;
Object registries$PlacedFeature = null; Object registries$PlacedFeature = null;
Object registries$JukeboxSong = null; Object registries$JukeboxSong = null;
Object registries$Recipe = null;
for (Field field : fields) { for (Field field : fields) {
Type fieldType = field.getGenericType(); Type fieldType = field.getGenericType();
if (fieldType instanceof ParameterizedType paramType) { if (fieldType instanceof ParameterizedType paramType) {
@@ -60,6 +63,8 @@ public final class MRegistries {
registries$RecipeType = field.get(null); registries$RecipeType = field.get(null);
} else if (rawType == CoreReflections.clazz$ConfiguredFeature) { } else if (rawType == CoreReflections.clazz$ConfiguredFeature) {
registries$ConfiguredFeature = field.get(null); registries$ConfiguredFeature = field.get(null);
} else if (rawType == CoreReflections.clazz$Recipe) {
registries$Recipe = field.get(null);
} }
} else { } else {
if (type == CoreReflections.clazz$Block) { if (type == CoreReflections.clazz$Block) {
@@ -88,20 +93,21 @@ public final class MRegistries {
} }
} }
} }
instance$Registries$BLOCK = requireNonNull(registries$Block); BLOCK = requireNonNull(registries$Block);
instance$Registries$ITEM = requireNonNull(registries$Item); ITEM = requireNonNull(registries$Item);
instance$Registries$ATTRIBUTE = requireNonNull(registries$Attribute); ATTRIBUTE = requireNonNull(registries$Attribute);
instance$Registries$BIOME = requireNonNull(registries$Biome); BIOME = requireNonNull(registries$Biome);
instance$Registries$MOB_EFFECT = requireNonNull(registries$MobEffect); MOB_EFFECT = requireNonNull(registries$MobEffect);
instance$Registries$SOUND_EVENT = requireNonNull(registries$SoundEvent); SOUND_EVENT = requireNonNull(registries$SoundEvent);
instance$Registries$DIMENSION_TYPE = requireNonNull(registries$DimensionType); DIMENSION_TYPE = requireNonNull(registries$DimensionType);
instance$Registries$PARTICLE_TYPE = requireNonNull(registries$ParticleType); PARTICLE_TYPE = requireNonNull(registries$ParticleType);
instance$Registries$ENTITY_TYPE = requireNonNull(registries$EntityType); ENTITY_TYPE = requireNonNull(registries$EntityType);
instance$Registries$FLUID = requireNonNull(registries$Fluid); FLUID = requireNonNull(registries$Fluid);
instance$Registries$RECIPE_TYPE = requireNonNull(registries$RecipeType); RECIPE_TYPE = requireNonNull(registries$RecipeType);
instance$Registries$CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature); CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature);
instance$Registries$PLACED_FEATURE = requireNonNull(registries$PlacedFeature); PLACED_FEATURE = requireNonNull(registries$PlacedFeature);
instance$Registries$JUKEBOX_SONG = registries$JukeboxSong; JUKEBOX_SONG = registries$JukeboxSong;
RECIPE = registries$Recipe;
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -9,6 +9,7 @@ import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils;
import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.Tag;
import net.momirealms.sparrow.nbt.codec.LegacyJavaOps;
import net.momirealms.sparrow.nbt.codec.LegacyNBTOps; import net.momirealms.sparrow.nbt.codec.LegacyNBTOps;
import net.momirealms.sparrow.nbt.codec.NBTOps; import net.momirealms.sparrow.nbt.codec.NBTOps;
@@ -34,8 +35,12 @@ public class MRegistryOps {
static { static {
try { try {
if (clazz$JavaOps != null) { if (clazz$JavaOps != null) {
// 1.20.5+
Object javaOps = ReflectionUtils.getDeclaredField(clazz$JavaOps, clazz$JavaOps, 0).get(null); Object javaOps = ReflectionUtils.getDeclaredField(clazz$JavaOps, clazz$JavaOps, 0).get(null);
JAVA = (DynamicOps<Object>) CoreReflections.method$RegistryOps$create.invoke(null, javaOps, FastNMS.INSTANCE.registryAccess()); JAVA = (DynamicOps<Object>) CoreReflections.method$RegistryOps$create.invoke(null, javaOps, FastNMS.INSTANCE.registryAccess());
} else if (!VersionHelper.isOrAbove1_20_5()) {
// 1.20.1-1.20.4
JAVA = (DynamicOps<Object>) CoreReflections.method$RegistryOps$create.invoke(null, LegacyJavaOps.INSTANCE, FastNMS.INSTANCE.registryAccess());
} else { } else {
JAVA = null; JAVA = null;
} }

View File

@@ -1,10 +1,13 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException;
import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils; import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils;
import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -968,7 +971,7 @@ public final class NetworkReflections {
); );
public static final Field field$ServerboundEditBookPacket$slot = requireNonNull( public static final Field field$ServerboundEditBookPacket$slot = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$ServerboundEditBookPacket, int.class, 0) ReflectionUtils.getDeclaredField(clazz$ServerboundEditBookPacket, int.class, VersionHelper.isOrAbove1_20_5() ? 0 : 4)
); );
public static final Field field$ServerboundEditBookPacket$pages = requireNonNull( public static final Field field$ServerboundEditBookPacket$pages = requireNonNull(
@@ -1293,4 +1296,177 @@ public final class NetworkReflections {
public static final Constructor<?> constructor$ClientboundTickingStatePacket = Optional.ofNullable(clazz$ClientboundTickingStatePacket) public static final Constructor<?> constructor$ClientboundTickingStatePacket = Optional.ofNullable(clazz$ClientboundTickingStatePacket)
.map(it -> ReflectionUtils.getConstructor(it, float.class, boolean.class)) .map(it -> ReflectionUtils.getConstructor(it, float.class, boolean.class))
.orElse(null); .orElse(null);
public static final Class<?> clazz$ClientboundBlockEventPacket = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"network.protocol.game.PacketPlayOutBlockAction",
"network.protocol.game.ClientboundBlockEventPacket"
)
);
public static final MethodHandle methodHandle$ServerboundRenameItemPacket$nameGetter;
public static final MethodHandle methodHandle$ServerboundRenameItemPacket$nameSetter;
public static final MethodHandle methodHandle$ServerboundHelloPacket$nameGetter;
public static final MethodHandle methodHandle$ServerboundHelloPacket$uuidGetter;
public static final MethodHandle methodHandle$ServerboundSetCreativeModeSlotPacket$itemStackGetter;
public static final MethodHandle methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter;
public static final MethodHandle methodHandle$ServerboundInteractPacket$actionGetter;
public static final MethodHandle methodHandle$ServerboundInteractPacket$InteractionAtLocationAction$handGetter;
public static final MethodHandle methodHandle$ServerboundInteractPacket$InteractionAtLocationAction$locationGetter;
public static final MethodHandle methodHandle$ServerboundSignUpdatePacket$linesGetter;
public static final MethodHandle methodHandle$ServerboundEditBookPacket$pagesGetter;
public static final MethodHandle methodHandle$ServerboundEditBookPacket$titleGetter;
public static final MethodHandle methodHandle$ServerboundEditBookPacket$slotGetter;
public static final MethodHandle methodHandle$ServerboundResourcePackPacket$actionGetter;
public static final MethodHandle methodHandle$ClientboundEntityEventPacket$entityIdGetter;
public static final MethodHandle methodHandle$ClientboundEntityEventPacket$eventIdGetter;
public static final MethodHandle methodHandle$ClientIntentionPacket$protocolVersionGetter;
public static final MethodHandle methodHandle$ClientboundRespawnPacket$dimensionGetter;
public static final MethodHandle methodHandle$ClientboundRespawnPacket$commonPlayerSpawnInfoGetter;
public static final MethodHandle methodHandle$CommonPlayerSpawnInfo$dimensionGetter;
public static final MethodHandle methodHandle$ClientboundLoginPacket$dimensionGetter;
public static final MethodHandle methodHandle$ClientboundLoginPacket$commonPlayerSpawnInfoGetter;
public static final MethodHandle methodHandle$ServerboundPickItemFromBlockPacket$posGetter;
public static final MethodHandle methodHandle$ServerboundPickItemFromEntityPacket$idGetter;
public static final MethodHandle methodHandle$ServerboundCustomPayloadPacket$payloadGetter;
static {
try {
methodHandle$ServerboundRenameItemPacket$nameGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundRenameItemPacket$name)
.asType(MethodType.methodType(String.class, Object.class))
);
methodHandle$ServerboundRenameItemPacket$nameSetter = requireNonNull(
ReflectionUtils.unreflectSetter(field$ServerboundRenameItemPacket$name)
.asType(MethodType.methodType(void.class, Object.class, String.class))
);
methodHandle$ServerboundHelloPacket$nameGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundHelloPacket$name)
.asType(MethodType.methodType(String.class, Object.class))
);
methodHandle$ServerboundHelloPacket$uuidGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundHelloPacket$uuid)
.asType(MethodType.methodType(VersionHelper.isOrAbove1_20_2() ? UUID.class : Optional.class, Object.class))
);
methodHandle$ServerboundSetCreativeModeSlotPacket$itemStackGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundSetCreativeModeSlotPacket$itemStack)
.asType(MethodType.methodType(Object.class, Object.class))
);
methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundSetCreativeModeSlotPacket$slotNum)
.asType(MethodType.methodType(VersionHelper.isOrAbove1_20_5() ? short.class : int.class, Object.class))
);
methodHandle$ServerboundInteractPacket$actionGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundInteractPacket$action)
.asType(MethodType.methodType(Object.class, Object.class))
);
methodHandle$ServerboundInteractPacket$InteractionAtLocationAction$handGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundInteractPacket$InteractionAtLocationAction$hand)
.asType(MethodType.methodType(Object.class, Object.class))
);
methodHandle$ServerboundInteractPacket$InteractionAtLocationAction$locationGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundInteractPacket$InteractionAtLocationAction$location)
.asType(MethodType.methodType(Object.class, Object.class))
);
methodHandle$ServerboundSignUpdatePacket$linesGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundSignUpdatePacket$lines)
.asType(MethodType.methodType(String[].class, Object.class))
);
methodHandle$ServerboundEditBookPacket$pagesGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundEditBookPacket$pages)
.asType(MethodType.methodType(List.class, Object.class))
);
methodHandle$ServerboundEditBookPacket$titleGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundEditBookPacket$title)
.asType(MethodType.methodType(Optional.class, Object.class))
);
methodHandle$ServerboundEditBookPacket$slotGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundEditBookPacket$slot)
.asType(MethodType.methodType(int.class, Object.class))
);
methodHandle$ServerboundResourcePackPacket$actionGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundResourcePackPacket$action)
.asType(MethodType.methodType(Object.class, Object.class))
);
methodHandle$ClientboundEntityEventPacket$entityIdGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundEntityEventPacket$entityId)
.asType(MethodType.methodType(int.class, Object.class))
);
methodHandle$ClientboundEntityEventPacket$eventIdGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundEntityEventPacket$eventId)
.asType(MethodType.methodType(byte.class, Object.class))
);
methodHandle$ClientIntentionPacket$protocolVersionGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientIntentionPacket$protocolVersion)
.asType(MethodType.methodType(int.class, Object.class))
);
if (field$ServerboundCustomPayloadPacket$payload != null) {
methodHandle$ServerboundCustomPayloadPacket$payloadGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundCustomPayloadPacket$payload)
.asType(MethodType.methodType(Object.class, Object.class))
);
} else {
methodHandle$ServerboundCustomPayloadPacket$payloadGetter = null;
}
if (field$ServerboundPickItemFromEntityPacket$id != null) {
methodHandle$ServerboundPickItemFromEntityPacket$idGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundPickItemFromEntityPacket$id)
.asType(MethodType.methodType(int.class, Object.class))
);
} else {
methodHandle$ServerboundPickItemFromEntityPacket$idGetter = null;
}
if (field$ServerboundPickItemFromBlockPacket$pos != null) {
methodHandle$ServerboundPickItemFromBlockPacket$posGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundPickItemFromBlockPacket$pos)
.asType(MethodType.methodType(Object.class, Object.class))
);
} else {
methodHandle$ServerboundPickItemFromBlockPacket$posGetter = null;
}
if (field$ClientboundLoginPacket$commonPlayerSpawnInfo != null) {
methodHandle$ClientboundLoginPacket$commonPlayerSpawnInfoGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundLoginPacket$commonPlayerSpawnInfo)
.asType(MethodType.methodType(Object.class, Object.class))
);
} else {
methodHandle$ClientboundLoginPacket$commonPlayerSpawnInfoGetter = null;
}
if (field$ClientboundLoginPacket$dimension != null) {
methodHandle$ClientboundLoginPacket$dimensionGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundLoginPacket$dimension)
.asType(MethodType.methodType(Object.class, Object.class))
);
} else {
methodHandle$ClientboundLoginPacket$dimensionGetter = null;
}
if (field$CommonPlayerSpawnInfo$dimension != null) {
methodHandle$CommonPlayerSpawnInfo$dimensionGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$CommonPlayerSpawnInfo$dimension)
.asType(MethodType.methodType(Object.class, Object.class))
);
} else {
methodHandle$CommonPlayerSpawnInfo$dimensionGetter = null;
}
if (field$ClientboundRespawnPacket$commonPlayerSpawnInfo != null) {
methodHandle$ClientboundRespawnPacket$commonPlayerSpawnInfoGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundRespawnPacket$commonPlayerSpawnInfo)
.asType(MethodType.methodType(Object.class, Object.class))
);
} else {
methodHandle$ClientboundRespawnPacket$commonPlayerSpawnInfoGetter = null;
}
if (field$ClientboundRespawnPacket$dimension != null) {
methodHandle$ClientboundRespawnPacket$dimensionGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundRespawnPacket$dimension)
.asType(MethodType.methodType(Object.class, Object.class))
);
} else {
methodHandle$ClientboundRespawnPacket$dimensionGetter = null;
}
} catch (Throwable e) {
throw new ReflectionInitException("Failed to initialize reflection", e);
}
}
} }

View File

@@ -30,10 +30,12 @@ import net.momirealms.craftengine.core.plugin.context.CooldownData;
import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.ProtocolVersion; import net.momirealms.craftengine.core.plugin.network.ProtocolVersion;
import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Position;
import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldEvents; import net.momirealms.craftengine.core.world.WorldEvents;
import org.bukkit.*; import org.bukkit.*;
@@ -138,17 +140,17 @@ public class BukkitServerPlayer extends Player {
@Override @Override
public Channel nettyChannel() { public Channel nettyChannel() {
return channel; return this.channel;
} }
@Override @Override
public CraftEngine plugin() { public CraftEngine plugin() {
return plugin; return this.plugin;
} }
@Override @Override
public boolean isMiningBlock() { public boolean isMiningBlock() {
return destroyPos != null; return this.destroyPos != null;
} }
public void setDestroyedState(Object destroyedState) { public void setDestroyedState(Object destroyedState) {
@@ -221,8 +223,8 @@ public class BukkitServerPlayer extends Player {
@Override @Override
public boolean updateLastSuccessfulInteractionTick(int tick) { public boolean updateLastSuccessfulInteractionTick(int tick) {
if (lastSuccessfulInteraction != tick) { if (this.lastSuccessfulInteraction != tick) {
lastSuccessfulInteraction = tick; this.lastSuccessfulInteraction = tick;
return true; return true;
} else { } else {
return false; return false;
@@ -231,7 +233,7 @@ public class BukkitServerPlayer extends Player {
@Override @Override
public int lastSuccessfulInteractionTick() { public int lastSuccessfulInteractionTick() {
return lastSuccessfulInteraction; return this.lastSuccessfulInteraction;
} }
@Override @Override
@@ -283,8 +285,13 @@ public class BukkitServerPlayer extends Player {
} }
@Override @Override
public void playSound(Key sound, float volume, float pitch) { public void playSound(Key sound, SoundSource source, float volume, float pitch) {
platformPlayer().playSound(platformPlayer(), sound.toString(), SoundCategory.MASTER, volume, pitch); platformPlayer().playSound(platformPlayer(), sound.toString(), SoundUtils.toBukkit(source), volume, pitch);
}
@Override
public void playSound(Key sound, BlockPos blockPos, SoundSource source, float volume, float pitch) {
platformPlayer().playSound(new Location(null, blockPos.x() + 0.5, blockPos.y() + 0.5, blockPos.z() + 0.5), sound.toString(), SoundUtils.toBukkit(source), volume, pitch);
} }
@Override @Override
@@ -535,8 +542,10 @@ public class BukkitServerPlayer extends Player {
public void abortMiningBlock() { public void abortMiningBlock() {
this.swingHandAck = false; this.swingHandAck = false;
this.miningProgress = 0; this.miningProgress = 0;
if (this.destroyPos != null) { BlockPos pos = this.destroyPos;
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1); if (pos != null && this.isDestroyingCustomBlock) {
// 只纠正自定义方块的
this.broadcastDestroyProgress(platformPlayer(), pos, LocationUtils.toBlockPos(pos), -1);
} }
} }
@@ -558,6 +567,8 @@ public class BukkitServerPlayer extends Player {
int currentTick = gameTicks(); int currentTick = gameTicks();
// optimize break speed, otherwise it would be too fast // optimize break speed, otherwise it would be too fast
if (currentTick - this.lastSuccessfulBreak <= 5) return; if (currentTick - this.lastSuccessfulBreak <= 5) return;
Object destroyedState = this.destroyedState;
if (destroyedState == null) return;
try { try {
org.bukkit.entity.Player player = platformPlayer(); org.bukkit.entity.Player player = platformPlayer();
double range = getCachedInteractionRange(); double range = getCachedInteractionRange();
@@ -575,7 +586,7 @@ public class BukkitServerPlayer extends Player {
// send hit sound if the sound is removed // send hit sound if the sound is removed
if (currentTick - this.lastHitBlockTime > 3) { if (currentTick - this.lastHitBlockTime > 3) {
Object blockOwner = FastNMS.INSTANCE.method$BlockState$getBlock(this.destroyedState); Object blockOwner = FastNMS.INSTANCE.method$BlockState$getBlock(destroyedState);
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(blockOwner); Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(blockOwner);
Object soundEvent = CoreReflections.field$SoundType$hitSound.get(soundType); Object soundEvent = CoreReflections.field$SoundType$hitSound.get(soundType);
Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent);
@@ -601,8 +612,8 @@ public class BukkitServerPlayer extends Player {
} }
} }
float progressToAdd = getDestroyProgress(this.destroyedState, hitPos); float progressToAdd = getDestroyProgress(destroyedState, hitPos);
int id = BlockStateUtils.blockStateToId(this.destroyedState); int id = BlockStateUtils.blockStateToId(destroyedState);
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(id); ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(id);
// double check custom block // double check custom block
if (customState != null && !customState.isEmpty()) { if (customState != null && !customState.isEmpty()) {
@@ -612,13 +623,13 @@ public class BukkitServerPlayer extends Player {
// it's correct on plugin side // it's correct on plugin side
if (blockSettings.isCorrectTool(item.id())) { if (blockSettings.isCorrectTool(item.id())) {
// but not on serverside // but not on serverside
if (!FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), this.destroyedState)) { if (!FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), destroyedState)) {
// we fix the speed // we fix the speed
progressToAdd = progressToAdd * (10f / 3f); progressToAdd = progressToAdd * (10f / 3f);
} }
} else { } else {
// not a correct tool on plugin side and not a correct tool on serverside // not a correct tool on plugin side and not a correct tool on serverside
if (!blockSettings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), this.destroyedState)) { if (!blockSettings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), destroyedState)) {
progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed(); progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed();
} }
} }
@@ -681,7 +692,7 @@ public class BukkitServerPlayer extends Player {
double d1 = (double) hitPos.y() - otherLocation.getY(); double d1 = (double) hitPos.y() - otherLocation.getY();
double d2 = (double) hitPos.z() - otherLocation.getZ(); double d2 = (double) hitPos.z() - otherLocation.getZ();
if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) { if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) {
this.plugin.networkManager().sendPacket(this.plugin.adapt(other), packet); FastNMS.INSTANCE.sendPacket(FastNMS.INSTANCE.field$Player$connection$connection(FastNMS.INSTANCE.method$CraftPlayer$getHandle(other)), packet);
} }
} }
} }
@@ -699,16 +710,20 @@ public class BukkitServerPlayer extends Player {
public void setIsDestroyingBlock(boolean is, boolean custom) { public void setIsDestroyingBlock(boolean is, boolean custom) {
this.miningProgress = 0; this.miningProgress = 0;
this.isDestroyingBlock = is; this.isDestroyingBlock = is;
this.isDestroyingCustomBlock = custom && is;
if (is) { if (is) {
this.swingHandAck = true; this.swingHandAck = true;
this.isDestroyingCustomBlock = custom;
} else { } else {
this.swingHandAck = false; this.swingHandAck = false;
this.destroyedState = null; this.destroyedState = null;
if (this.destroyPos != null) { if (this.destroyPos != null) {
// 只纠正自定义方块的
if (this.isDestroyingCustomBlock) {
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1); this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
}
this.destroyPos = null; this.destroyPos = null;
} }
this.isDestroyingCustomBlock = false;
} }
} }

View File

@@ -26,7 +26,7 @@ public class BukkitSoundManager extends AbstractSoundManager {
protected void registerSongs(Map<Key, JukeboxSong> songs) { protected void registerSongs(Map<Key, JukeboxSong> songs) {
if (songs.isEmpty()) return; if (songs.isEmpty()) return;
try { try {
Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$JUKEBOX_SONG);; Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.JUKEBOX_SONG);;
unfreezeRegistry(registry); unfreezeRegistry(registry);
for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) { for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) {
Key id = entry.getKey(); Key id = entry.getKey();

View File

@@ -2,8 +2,10 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; 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.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.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.*;
@@ -22,18 +24,26 @@ import org.jetbrains.annotations.Nullable;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
public class BlockStateUtils { public class BlockStateUtils {
public static final IdentityHashMap<Object, Object> CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>(); public static final IdentityHashMap<Object, Object> CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>();
private static int vanillaStateSize; private static int vanillaStateSize;
private static boolean hasInit; private static boolean hasInit;
public static Map<Object, Integer> IGNITE_ODDS;
@SuppressWarnings("unchecked")
public static void init(int size) { public static void init(int size) {
if (hasInit) { if (hasInit) {
throw new IllegalStateException("BlockStateUtils has already been initialized"); throw new IllegalStateException("BlockStateUtils has already been initialized");
} }
vanillaStateSize = size; vanillaStateSize = size;
try {
IGNITE_ODDS = (Map<Object, Integer>) CoreReflections.field$FireBlock$igniteOdds.get(MBlocks.FIRE);
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to initialize instance$FireBlock$igniteOdds", e);
}
hasInit = true; hasInit = true;
} }
@@ -248,4 +258,9 @@ public class BlockStateUtils {
public static int vanillaStateSize() { public static int vanillaStateSize() {
return vanillaStateSize; return vanillaStateSize;
} }
public static boolean isBurnable(Object state) {
Object blockOwner = getBlockOwner(state);
return IGNITE_ODDS.getOrDefault(blockOwner, 0) > 0;
}
} }

View File

@@ -16,7 +16,7 @@ public class BlockTags {
Object value = CACHE.get(key); Object value = CACHE.get(key);
if (value == null) { if (value == null) {
try { try {
value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, KeyUtils.toResourceLocation(key)); value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(key));
CACHE.put(key, value); CACHE.put(key, value);
return value; return value;
} catch (Exception e) { } catch (Exception e) {

View File

@@ -2,11 +2,8 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
public final class BukkitReflectionUtils { public final class BukkitReflectionUtils {

View File

@@ -0,0 +1,85 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.util.DamageSource;
import org.bukkit.event.entity.EntityDamageEvent;
public class DamageCauseUtils {
private DamageCauseUtils() {}
public static EntityDamageEvent.DamageCause toBukkit(DamageSource cause) {
return switch (cause) {
case BLOCK_EXPLOSION -> EntityDamageEvent.DamageCause.BLOCK_EXPLOSION;
case CAMPFIRE -> EntityDamageEvent.DamageCause.CAMPFIRE;
case CONTACT -> EntityDamageEvent.DamageCause.CONTACT;
case CRAMMING -> EntityDamageEvent.DamageCause.CRAMMING;
case CUSTOM -> EntityDamageEvent.DamageCause.CUSTOM;
case DROWNING -> EntityDamageEvent.DamageCause.DROWNING;
case DRYOUT -> EntityDamageEvent.DamageCause.DRYOUT;
case ENTITY_ATTACK -> EntityDamageEvent.DamageCause.ENTITY_ATTACK;
case ENTITY_EXPLOSION -> EntityDamageEvent.DamageCause.ENTITY_EXPLOSION;
case ENTITY_SWEEP_ATTACK -> EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK;
case FALL -> EntityDamageEvent.DamageCause.FALL;
case FALLING_BLOCK -> EntityDamageEvent.DamageCause.FALLING_BLOCK;
case FIRE -> EntityDamageEvent.DamageCause.FIRE;
case FIRE_TICK -> EntityDamageEvent.DamageCause.FIRE_TICK;
case FLY_INTO_WALL -> EntityDamageEvent.DamageCause.FLY_INTO_WALL;
case FREEZE -> EntityDamageEvent.DamageCause.FREEZE;
case HOT_FLOOR -> EntityDamageEvent.DamageCause.HOT_FLOOR;
case KILL -> EntityDamageEvent.DamageCause.KILL;
case LAVA -> EntityDamageEvent.DamageCause.LAVA;
case LIGHTNING -> EntityDamageEvent.DamageCause.LIGHTNING;
case MAGIC -> EntityDamageEvent.DamageCause.MAGIC;
case MELTING -> EntityDamageEvent.DamageCause.MELTING;
case POISON -> EntityDamageEvent.DamageCause.POISON;
case PROJECTILE -> EntityDamageEvent.DamageCause.PROJECTILE;
case SONIC_BOOM -> EntityDamageEvent.DamageCause.SONIC_BOOM;
case STARVATION -> EntityDamageEvent.DamageCause.STARVATION;
case SUFFOCATION -> EntityDamageEvent.DamageCause.SUFFOCATION;
case SUICIDE -> EntityDamageEvent.DamageCause.SUICIDE;
case THORNS -> EntityDamageEvent.DamageCause.THORNS;
case VOID -> EntityDamageEvent.DamageCause.VOID;
case WITHER -> EntityDamageEvent.DamageCause.WITHER;
case WORLD_BORDER -> EntityDamageEvent.DamageCause.WORLD_BORDER;
default -> null;
};
}
public static DamageSource fromBukkit(EntityDamageEvent.DamageCause cause) {
return switch (cause) {
case BLOCK_EXPLOSION -> DamageSource.BLOCK_EXPLOSION;
case CAMPFIRE -> DamageSource.CAMPFIRE;
case CONTACT -> DamageSource.CONTACT;
case CRAMMING -> DamageSource.CRAMMING;
case CUSTOM -> DamageSource.CUSTOM;
case DROWNING -> DamageSource.DROWNING;
case DRYOUT -> DamageSource.DRYOUT;
case ENTITY_ATTACK -> DamageSource.ENTITY_ATTACK;
case ENTITY_EXPLOSION -> DamageSource.ENTITY_EXPLOSION;
case ENTITY_SWEEP_ATTACK -> DamageSource.ENTITY_SWEEP_ATTACK;
case FALL -> DamageSource.FALL;
case FALLING_BLOCK -> DamageSource.FALLING_BLOCK;
case FIRE -> DamageSource.FIRE;
case FIRE_TICK -> DamageSource.FIRE_TICK;
case FLY_INTO_WALL -> DamageSource.FLY_INTO_WALL;
case FREEZE -> DamageSource.FREEZE;
case HOT_FLOOR -> DamageSource.HOT_FLOOR;
case KILL -> DamageSource.KILL;
case LAVA -> DamageSource.LAVA;
case LIGHTNING -> DamageSource.LIGHTNING;
case MAGIC -> DamageSource.MAGIC;
case MELTING -> DamageSource.MELTING;
case POISON -> DamageSource.POISON;
case PROJECTILE -> DamageSource.PROJECTILE;
case SONIC_BOOM -> DamageSource.SONIC_BOOM;
case STARVATION -> DamageSource.STARVATION;
case SUFFOCATION -> DamageSource.SUFFOCATION;
case SUICIDE -> DamageSource.SUICIDE;
case THORNS -> DamageSource.THORNS;
case VOID -> DamageSource.VOID;
case WITHER -> DamageSource.WITHER;
case WORLD_BORDER -> DamageSource.WORLD_BORDER;
default -> null;
};
}
}

View File

@@ -10,7 +10,7 @@ public class FeatureUtils {
public static Object createFeatureKey(Key id) { public static Object createFeatureKey(Key id) {
try { try {
return CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$CONFIGURED_FEATURE, KeyUtils.toResourceLocation(id)); return CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.CONFIGURED_FEATURE, KeyUtils.toResourceLocation(id));
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -92,6 +92,7 @@ public class InteractUtils {
keyReference, item.getItem() keyReference, item.getItem()
))) != null).isPresent(); ))) != null).isPresent();
}); });
registerInteraction(BlockKeys.DECORATED_POT, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.HOPPER, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.HOPPER, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.DISPENSER, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.DISPENSER, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.DROPPER, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.DROPPER, (player, item, blockState, result) -> true);

View File

@@ -19,7 +19,7 @@ public class ItemTags {
Object value = CACHE.get(key); Object value = CACHE.get(key);
if (value == null) { if (value == null) {
try { try {
value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$ITEM, KeyUtils.toResourceLocation(key)); value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.ITEM, KeyUtils.toResourceLocation(key));
CACHE.put(key, value); CACHE.put(key, value);
return value; return value;
} catch (Exception e) { } catch (Exception e) {

View File

@@ -1,19 +1,19 @@
package net.momirealms.craftengine.bukkit.util; package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.plugin.config.Config;
public class NoteBlockChainUpdateUtils { public class NoteBlockChainUpdateUtils {
private NoteBlockChainUpdateUtils() {} private NoteBlockChainUpdateUtils() {}
// TODO 都在一个区块内应该优化到区块内的方块getter
public static void noteBlockChainUpdate(Object level, Object chunkSource, Object direction, Object blockPos, int times) { public static void noteBlockChainUpdate(Object level, Object chunkSource, Object direction, Object blockPos, int times) {
if (times >= Config.maxChainUpdate()) return; if (times-- < 0) return;
Object relativePos = FastNMS.INSTANCE.method$BlockPos$relative(blockPos, direction); Object relativePos = FastNMS.INSTANCE.method$BlockPos$relative(blockPos, direction);
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos); Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos);
if (BlockStateUtils.isClientSideNoteBlock(state)) { if (BlockStateUtils.isClientSideNoteBlock(state)) {
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, relativePos); FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, relativePos);
noteBlockChainUpdate(level, chunkSource, direction, relativePos, times+1); noteBlockChainUpdate(level, chunkSource, direction, relativePos, times);
} }
} }
} }

View File

@@ -19,7 +19,7 @@ public class RegistryUtils {
public static int currentBiomeRegistrySize() { public static int currentBiomeRegistrySize() {
try { try {
Object idMap = CoreReflections.method$Registry$asHolderIdMap.invoke(CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$BIOME)); Object idMap = CoreReflections.method$Registry$asHolderIdMap.invoke(CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.BIOME));
return (int) CoreReflections.method$IdMap$size.invoke(idMap); return (int) CoreReflections.method$IdMap$size.invoke(idMap);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@@ -1,20 +1,14 @@
package net.momirealms.craftengine.bukkit.world; package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.util.SoundUtils;
import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockInWorld; import net.momirealms.craftengine.core.world.*;
import net.momirealms.craftengine.core.world.Position;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldHeight;
import net.momirealms.craftengine.core.world.particle.ParticleData; import net.momirealms.craftengine.core.world.particle.ParticleData;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Particle; import org.bukkit.Particle;
@@ -124,4 +118,9 @@ public class BukkitWorld implements World {
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z); Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z);
FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState.handle(), flags); FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState.handle(), flags);
} }
@Override
public void levelEvent(int id, BlockPos pos, int data) {
FastNMS.INSTANCE.method$Level$levelEvent(serverWorld(), id, LocationUtils.toBlockPos(pos), data);
}
} }

View File

@@ -182,6 +182,20 @@ debug_clear_cooldown:
- /craftengine debug clear-cooldown - /craftengine debug clear-cooldown
- /ce debug clear-cooldown - /ce debug clear-cooldown
debug_entity_id_to_uuid:
enable: true
permission: ce.command.debug.entity_id_to_uuid
usage:
- /craftengine debug entity-id-to-uuid
- /ce debug entity-id-to-uuid
debug_migrate_templates:
enable: true
permission: ce.command.debug.migrate_templates
usage:
- /craftengine debug migrate-templates
- /ce debug migrate-templates
debug_test: debug_test:
enable: true enable: true
permission: ce.command.debug.test permission: ce.command.debug.test

View File

@@ -49,12 +49,14 @@ resource-pack:
bypass-textures: bypass-textures:
# - minecraft:block/farmland # - minecraft:block/farmland
- "@legacy_unicode" - "@legacy_unicode"
- "@vanilla_font_textures" - "@vanilla_textures"
- "@vanilla_item_textures" bypass-models:
- "@vanilla_block_textures" - "@vanilla_models"
bypass-models: []
bypass-sounds: [] bypass-sounds: []
bypass-equipments: [] bypass-equipments: []
# Validate if there are any errors in the resource pack, such as missing textures or models
validate:
enable: true
supported-version: supported-version:
min: "1.20" min: "1.20"
max: LATEST max: LATEST
@@ -336,7 +338,7 @@ gui:
performance: performance:
# Maximum chain update depth when fixing client visuals # Maximum chain update depth when fixing client visuals
max-block-chain-update-limit: 64 max-note-block-chain-update-limit: 48
# Prevent lag or oversized packet when processing emoji-heavy content # Prevent lag or oversized packet when processing emoji-heavy content
max-emojis-per-parse: 16 max-emojis-per-parse: 16

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,7 @@ minecraft:jungle_sapling[stage=1]: minecraft:jungle_sapling[stage=0]
minecraft:dark_oak_sapling[stage=1]: minecraft:dark_oak_sapling[stage=0] minecraft:dark_oak_sapling[stage=1]: minecraft:dark_oak_sapling[stage=0]
minecraft:acacia_sapling[stage=1]: minecraft:acacia_sapling[stage=0] minecraft:acacia_sapling[stage=1]: minecraft:acacia_sapling[stage=0]
minecraft:cherry_sapling[stage=1]: minecraft:cherry_sapling[stage=0] minecraft:cherry_sapling[stage=1]: minecraft:cherry_sapling[stage=0]
#minecraft:pale_oak_leaves[stage=1]: minecraft:pale_oak_leaves[stage=0] # 1.21.4+ #minecraft:pale_oak_sapling[stage=1]: minecraft:pale_oak_sapling[stage=0] # 1.21.4+
######################################################################################################################################################################################################################## ########################################################################################################################################################################################################################
# Ideal block for lower half slabs # Ideal block for lower half slabs
# Sculk Sensor # Sculk Sensor

View File

@@ -167,7 +167,7 @@ items#misc:
template: "default:loot_table/self" template: "default:loot_table/self"
settings: settings:
template: template:
- default:sound/sand - default:sound/stone
- default:pickaxe_power/level_1 - default:pickaxe_power/level_1
- default:settings/solid_1x1x1 - default:settings/solid_1x1x1
overrides: overrides:

View File

@@ -2,7 +2,7 @@ templates:
default:emoji/basic: default:emoji/basic:
content: "<hover:show_text:'<i18n:emoji.tip>'><!shadow><white><arg:emoji></white></!shadow></hover>" content: "<hover:show_text:'<i18n:emoji.tip>'><!shadow><white><arg:emoji></white></!shadow></hover>"
default:emoji/addition_info: default:emoji/addition_info:
content: "<hover:show_text:'<i18n:emoji.tip>'><!shadow><white><arg:emoji></white></!shadow>{text}</hover>" content: "<hover:show_text:'<i18n:emoji.tip>'><!shadow><white><arg:emoji></white></!shadow>${text}</hover>"
emoji: emoji:
default:emoji_location: default:emoji_location:

View File

@@ -1,5 +1,4 @@
items: items:
# client-bound-data requires CraftEngine mod to apply
minecraft:string: minecraft:string:
client-bound-data: client-bound-data:
components: components:

View File

@@ -229,16 +229,16 @@ items#topaz_gears:
templates: templates:
default:armor/topaz: default:armor/topaz:
material: "chainmail_{part}" material: "chainmail_${part}"
custom-model-data: 1000 custom-model-data: 1000
data: data:
item-name: "<!i><#FF8C00><i18n:item.topaz_{part}>" item-name: "<!i><#FF8C00><i18n:item.topaz_${part}>"
tooltip-style: minecraft:topaz tooltip-style: minecraft:topaz
settings: settings:
tags: tags:
- "default:topaz_tools" - "default:topaz_tools"
equippable: equippable:
slot: "{slot}" slot: "${slot}"
asset-id: topaz asset-id: topaz
humanoid: "minecraft:topaz" humanoid: "minecraft:topaz"
humanoid-leggings: "minecraft:topaz" humanoid-leggings: "minecraft:topaz"
@@ -247,111 +247,111 @@ templates:
property: minecraft:trim_material property: minecraft:trim_material
fallback: fallback:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}" path: "minecraft:item/custom/topaz_${part}"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
cases: cases:
- when: minecraft:quartz - when: minecraft:quartz
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_quartz_trim" path: "minecraft:item/custom/topaz_${part}_quartz_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_quartz" "layer1": "minecraft:trims/items/${part}_trim_quartz"
- when: minecraft:iron - when: minecraft:iron
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_iron_trim" path: "minecraft:item/custom/topaz_${part}_iron_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_iron" "layer1": "minecraft:trims/items/${part}_trim_iron"
- when: minecraft:netherite - when: minecraft:netherite
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_netherite_trim" path: "minecraft:item/custom/topaz_${part}_netherite_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_netherite" "layer1": "minecraft:trims/items/${part}_trim_netherite"
- when: minecraft:redstone - when: minecraft:redstone
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_redstone_trim" path: "minecraft:item/custom/topaz_${part}_redstone_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_redstone" "layer1": "minecraft:trims/items/${part}_trim_redstone"
- when: minecraft:copper - when: minecraft:copper
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_copper_trim" path: "minecraft:item/custom/topaz_${part}_copper_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_copper" "layer1": "minecraft:trims/items/${part}_trim_copper"
- when: minecraft:gold - when: minecraft:gold
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_gold_trim" path: "minecraft:item/custom/topaz_${part}_gold_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_gold" "layer1": "minecraft:trims/items/${part}_trim_gold"
- when: minecraft:emerald - when: minecraft:emerald
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_emerald_trim" path: "minecraft:item/custom/topaz_${part}_emerald_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_emerald" "layer1": "minecraft:trims/items/${part}_trim_emerald"
- when: minecraft:diamond - when: minecraft:diamond
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_diamond_trim" path: "minecraft:item/custom/topaz_${part}_diamond_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_diamond" "layer1": "minecraft:trims/items/${part}_trim_diamond"
- when: minecraft:lapis - when: minecraft:lapis
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_lapis_trim" path: "minecraft:item/custom/topaz_${part}_lapis_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_lapis" "layer1": "minecraft:trims/items/${part}_trim_lapis"
- when: minecraft:amethyst - when: minecraft:amethyst
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_amethyst_trim" path: "minecraft:item/custom/topaz_${part}_amethyst_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_amethyst" "layer1": "minecraft:trims/items/${part}_trim_amethyst"
- when: minecraft:resin - when: minecraft:resin
model: model:
type: minecraft:model type: minecraft:model
path: "minecraft:item/custom/topaz_{part}_resin_trim" path: "minecraft:item/custom/topaz_${part}_resin_trim"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "minecraft:item/custom/topaz_{part}" "layer0": "minecraft:item/custom/topaz_${part}"
"layer1": "minecraft:trims/items/{part}_trim_resin" "layer1": "minecraft:trims/items/${part}_trim_resin"
recipes#11: recipes#11:
default:topaz_shovel: default:topaz_shovel:

View File

@@ -7,32 +7,32 @@ templates#models#block:
# model: model_path # model: model_path
# texture: texture_path # texture: texture_path
default:model/cube_all: default:model/cube_all:
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:block/cube_all" parent: "minecraft:block/cube_all"
textures: textures:
"all": "{texture}" "all": "${texture}"
# template: default:model/simplified_cube_all # template: default:model/simplified_cube_all
# arguments: # arguments:
# path: [model/texture]_path # path: [model/texture]_path
default:model/simplified_cube_all: default:model/simplified_cube_all:
path: "{path}" path: "${path}"
generation: generation:
parent: "minecraft:block/cube_all" parent: "minecraft:block/cube_all"
textures: textures:
"all": "{path}" "all": "${path}"
# template: default:model/cube_column # template: default:model/cube_column
# arguments: # arguments:
# model: model_path # model: model_path
# end_texture: end_texture_path # end_texture: end_texture_path
# side_texture: side_texture_path # side_texture: side_texture_path
default:model/cube_column: default:model/cube_column:
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:block/cube_column" parent: "minecraft:block/cube_column"
textures: textures:
"end": "{end_texture}" "end": "${end_texture}"
"side": "{side_texture}" "side": "${side_texture}"
# template: default:model/cube # template: default:model/cube
# arguments: # arguments:
# model: model_path # model: model_path
@@ -44,7 +44,7 @@ templates#models#block:
# south_texture: south_texture_path # south_texture: south_texture_path
# west_texture: west_texture_path # west_texture: west_texture_path
default:model/cube: default:model/cube:
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:block/cube_column" parent: "minecraft:block/cube_column"
textures: textures:
@@ -64,21 +64,21 @@ templates#models#2d:
# texture: texture_path # texture: texture_path
default:model/generated: default:model/generated:
type: "minecraft:model" type: "minecraft:model"
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "{texture}" "layer0": "${texture}"
# template: default:model/simplified_generated # template: default:model/simplified_generated
# arguments: # arguments:
# path: [model/texture]_path # path: [model/texture]_path
default:model/simplified_generated: default:model/simplified_generated:
type: "minecraft:model" type: "minecraft:model"
path: "{path}" path: "${path}"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "{path}" "layer0": "${path}"
# template: default:model/2_layer_generated # template: default:model/2_layer_generated
# arguments: # arguments:
# model: model_path # model: model_path
@@ -86,33 +86,33 @@ templates#models#2d:
# layer1: texture_path # layer1: texture_path
default:model/2_layer_generated: default:model/2_layer_generated:
type: "minecraft:model" type: "minecraft:model"
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "{layer0}" "layer0": "${layer0}"
"layer1": "{layer1}" "layer1": "${layer1}"
# template: default:model/handheld # template: default:model/handheld
# arguments: # arguments:
# model: model_path # model: model_path
# texture: texture_path # texture: texture_path
default:model/handheld: default:model/handheld:
type: "minecraft:model" type: "minecraft:model"
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:item/handheld" parent: "minecraft:item/handheld"
textures: textures:
"layer0": "{texture}" "layer0": "${texture}"
# template: default:model/simplified_handheld # template: default:model/simplified_handheld
# arguments: # arguments:
# path: [model/texture]_path # path: [model/texture]_path
default:model/simplified_handheld: default:model/simplified_handheld:
type: "minecraft:model" type: "minecraft:model"
path: "{path}" path: "${path}"
generation: generation:
parent: "minecraft:item/handheld" parent: "minecraft:item/handheld"
textures: textures:
"layer0": "{path}" "layer0": "${path}"
# template: default:model/elytra # template: default:model/elytra
# arguments: # arguments:
# model: model_path # model: model_path
@@ -123,17 +123,17 @@ templates#models#2d:
type: "minecraft:condition" type: "minecraft:condition"
property: minecraft:broken property: minecraft:broken
on-false: on-false:
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "{texture}" "layer0": "${texture}"
on-true: on-true:
path: "{broken_model}" path: "${broken_model}"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "{broken_texture}" "layer0": "${broken_texture}"
# template: default:model/simplified_elytra # template: default:model/simplified_elytra
# arguments: # arguments:
# path: [model/texture]_path # path: [model/texture]_path
@@ -141,10 +141,10 @@ templates#models#2d:
default:model/simplified_elytra: default:model/simplified_elytra:
template: default:model/elytra template: default:model/elytra
arguments: arguments:
model: "{path}" model: "${path}"
texture: "{path}" texture: "${path}"
broken_model: "{broken_path}" broken_model: "${broken_path}"
broken_texture: "{broken_path}" broken_texture: "${broken_path}"
# shield # shield
templates#models#shield: templates#models#shield:
@@ -157,10 +157,10 @@ templates#models#shield:
property: "minecraft:using_item" property: "minecraft:using_item"
on-false: on-false:
type: minecraft:model type: minecraft:model
path: "{model}" path: "${model}"
on-true: on-true:
type: minecraft:model type: minecraft:model
path: "{block_model}" path: "${block_model}"
# fishing rods # fishing rods
templates#models#fishing_rod: templates#models#fishing_rod:
@@ -173,10 +173,10 @@ templates#models#fishing_rod:
property: "minecraft:fishing_rod/cast" property: "minecraft:fishing_rod/cast"
on-false: on-false:
type: "minecraft:model" type: "minecraft:model"
path: "{model}" path: "${model}"
on-true: on-true:
type: "minecraft:model" type: "minecraft:model"
path: "{cast_model}" path: "${cast_model}"
# template: default:model/fishing_rod_2d # template: default:model/fishing_rod_2d
# arguments: # arguments:
# model: rod_model_path # model: rod_model_path
@@ -188,18 +188,18 @@ templates#models#fishing_rod:
property: "minecraft:fishing_rod/cast" property: "minecraft:fishing_rod/cast"
on-false: on-false:
type: "minecraft:model" type: "minecraft:model"
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:item/fishing_rod" parent: "minecraft:item/fishing_rod"
textures: textures:
"layer0": "{texture}" "layer0": "${texture}"
on-true: on-true:
type: "minecraft:model" type: "minecraft:model"
path: "{cast_model}" path: "${cast_model}"
generation: generation:
parent: "minecraft:item/fishing_rod" parent: "minecraft:item/fishing_rod"
textures: textures:
"layer0": "{cast_texture}" "layer0": "${cast_texture}"
# template: default:model/simplified_fishing_rod_2d # template: default:model/simplified_fishing_rod_2d
# arguments: # arguments:
# path: rod_[model/texture]_path # path: rod_[model/texture]_path
@@ -207,10 +207,10 @@ templates#models#fishing_rod:
default:model/simplified_fishing_rod_2d: default:model/simplified_fishing_rod_2d:
template: default:model/fishing_rod_2d template: default:model/fishing_rod_2d
arguments: arguments:
texture: "{path}" texture: "${path}"
model: "{path}" model: "${path}"
cast_texture: "{cast_path}" cast_texture: "${cast_path}"
cast_model: "{cast_path}" cast_model: "${cast_path}"
# bows # bows
templates#models#bow: templates#models#bow:
@@ -225,7 +225,7 @@ templates#models#bow:
property: "minecraft:using_item" property: "minecraft:using_item"
on-false: on-false:
type: "minecraft:model" type: "minecraft:model"
path: "{model}" path: "${model}"
on-true: on-true:
type: "minecraft:range_dispatch" type: "minecraft:range_dispatch"
property: "minecraft:use_duration" property: "minecraft:use_duration"
@@ -233,15 +233,15 @@ templates#models#bow:
entries: entries:
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_1_model}" path: "${pulling_1_model}"
threshold: 0.65 threshold: 0.65
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_2_model}" path: "${pulling_2_model}"
threshold: 0.9 threshold: 0.9
fallback: fallback:
type: minecraft:model type: minecraft:model
path: "{pulling_0_model}" path: "${pulling_0_model}"
# template: default:model/bow_2d # template: default:model/bow_2d
# arguments: # arguments:
# model: bow_model_path # model: bow_model_path
@@ -257,11 +257,11 @@ templates#models#bow:
property: "minecraft:using_item" property: "minecraft:using_item"
on-false: on-false:
type: "minecraft:model" type: "minecraft:model"
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:item/bow" parent: "minecraft:item/bow"
textures: textures:
"layer0": "{texture}" "layer0": "${texture}"
on-true: on-true:
type: "minecraft:range_dispatch" type: "minecraft:range_dispatch"
property: "minecraft:use_duration" property: "minecraft:use_duration"
@@ -269,27 +269,27 @@ templates#models#bow:
entries: entries:
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_1_model}" path: "${pulling_1_model}"
generation: generation:
parent: "minecraft:item/bow_pulling_1" parent: "minecraft:item/bow_pulling_1"
textures: textures:
"layer0": "{pulling_1_texture}" "layer0": "${pulling_1_texture}"
threshold: 0.65 threshold: 0.65
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_2_model}" path: "${pulling_2_model}"
generation: generation:
parent: "minecraft:item/bow_pulling_2" parent: "minecraft:item/bow_pulling_2"
textures: textures:
"layer0": "{pulling_2_texture}" "layer0": "${pulling_2_texture}"
threshold: 0.9 threshold: 0.9
fallback: fallback:
type: minecraft:model type: minecraft:model
path: "{pulling_0_model}" path: "${pulling_0_model}"
generation: generation:
parent: "minecraft:item/bow_pulling_0" parent: "minecraft:item/bow_pulling_0"
textures: textures:
"layer0": "{pulling_0_texture}" "layer0": "${pulling_0_texture}"
# template: default:model/simplified_bow_2d # template: default:model/simplified_bow_2d
# arguments: # arguments:
# path: bow_[model/texture]_path # path: bow_[model/texture]_path
@@ -299,14 +299,14 @@ templates#models#bow:
default:model/simplified_bow_2d: default:model/simplified_bow_2d:
template: default:model/bow_2d template: default:model/bow_2d
arguments: arguments:
model: "{path}" model: "${path}"
pulling_0_model: "{pulling_0_path}" pulling_0_model: "${pulling_0_path}"
pulling_1_model: "{pulling_1_path}" pulling_1_model: "${pulling_1_path}"
pulling_2_model: "{pulling_2_path}" pulling_2_model: "${pulling_2_path}"
texture: "{path}" texture: "${path}"
pulling_0_texture: "{pulling_0_path}" pulling_0_texture: "${pulling_0_path}"
pulling_1_texture: "{pulling_1_path}" pulling_1_texture: "${pulling_1_path}"
pulling_2_texture: "{pulling_2_path}" pulling_2_texture: "${pulling_2_path}"
# crossbows # crossbows
templates#models#crossbow: templates#models#crossbow:
@@ -328,29 +328,29 @@ templates#models#crossbow:
- when: arrow - when: arrow
model: model:
type: minecraft:model type: minecraft:model
path: "{arrow_model}" path: "${arrow_model}"
- when: rocket - when: rocket
model: model:
type: minecraft:model type: minecraft:model
path: "{firework_model}" path: "${firework_model}"
fallback: fallback:
type: minecraft:model type: minecraft:model
path: "{model}" path: "${model}"
on-true: on-true:
type: "minecraft:range_dispatch" type: "minecraft:range_dispatch"
property: "minecraft:crossbow/pull" property: "minecraft:crossbow/pull"
entries: entries:
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_1_model}" path: "${pulling_1_model}"
threshold: 0.58 threshold: 0.58
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_2_model}" path: "${pulling_2_model}"
threshold: 1.0 threshold: 1.0
fallback: fallback:
type: minecraft:model type: minecraft:model
path: "{pulling_0_model}" path: "${pulling_0_model}"
# template: default:model/crossbow_2d # template: default:model/crossbow_2d
# arguments: # arguments:
# model: crossbow_model_path # model: crossbow_model_path
@@ -375,53 +375,53 @@ templates#models#crossbow:
- when: arrow - when: arrow
model: model:
type: minecraft:model type: minecraft:model
path: "{arrow_model}" path: "${arrow_model}"
generation: generation:
parent: "minecraft:item/crossbow_arrow" parent: "minecraft:item/crossbow_arrow"
textures: textures:
"layer0": "{arrow_texture}" "layer0": "${arrow_texture}"
- when: rocket - when: rocket
model: model:
type: minecraft:model type: minecraft:model
path: "{firework_model}" path: "${firework_model}"
generation: generation:
parent: "minecraft:item/crossbow_firework" parent: "minecraft:item/crossbow_firework"
textures: textures:
"layer0": "{firework_texture}" "layer0": "${firework_texture}"
fallback: fallback:
type: minecraft:model type: minecraft:model
path: "{model}" path: "${model}"
generation: generation:
parent: "minecraft:item/crossbow" parent: "minecraft:item/crossbow"
textures: textures:
"layer0": "{texture}" "layer0": "${texture}"
on-true: on-true:
type: "minecraft:range_dispatch" type: "minecraft:range_dispatch"
property: "minecraft:crossbow/pull" property: "minecraft:crossbow/pull"
entries: entries:
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_1_model}" path: "${pulling_1_model}"
generation: generation:
parent: "minecraft:item/crossbow_pulling_1" parent: "minecraft:item/crossbow_pulling_1"
textures: textures:
"layer0": "{pulling_1_texture}" "layer0": "${pulling_1_texture}"
threshold: 0.58 threshold: 0.58
- model: - model:
type: minecraft:model type: minecraft:model
path: "{pulling_2_model}" path: "${pulling_2_model}"
generation: generation:
parent: "minecraft:item/crossbow_pulling_2" parent: "minecraft:item/crossbow_pulling_2"
textures: textures:
"layer0": "{pulling_2_texture}" "layer0": "${pulling_2_texture}"
threshold: 1.0 threshold: 1.0
fallback: fallback:
type: minecraft:model type: minecraft:model
path: "{pulling_0_model}" path: "${pulling_0_model}"
generation: generation:
parent: "minecraft:item/crossbow_pulling_0" parent: "minecraft:item/crossbow_pulling_0"
textures: textures:
"layer0": "{pulling_0_texture}" "layer0": "${pulling_0_texture}"
# template: default:model/simplified_crossbow_2d # template: default:model/simplified_crossbow_2d
# arguments: # arguments:
# path: crossbow_[model/texture]_path # path: crossbow_[model/texture]_path
@@ -433,28 +433,28 @@ templates#models#crossbow:
default:model/simplified_crossbow_2d: default:model/simplified_crossbow_2d:
template: default:model/crossbow_2d template: default:model/crossbow_2d
arguments: arguments:
model: "{path}" model: "${path}"
texture: "{path}" texture: "${path}"
arrow_model: "{arrow_path}" arrow_model: "${arrow_path}"
arrow_texture: "{arrow_path}" arrow_texture: "${arrow_path}"
firework_model: "{firework_path}" firework_model: "${firework_path}"
firework_texture: "{firework_path}" firework_texture: "${firework_path}"
pulling_0_model: "{pulling_0_path}" pulling_0_model: "${pulling_0_path}"
pulling_0_texture: "{pulling_0_path}" pulling_0_texture: "${pulling_0_path}"
pulling_1_model: "{pulling_1_path}" pulling_1_model: "${pulling_1_path}"
pulling_1_texture: "{pulling_1_path}" pulling_1_texture: "${pulling_1_path}"
pulling_2_model: "{pulling_2_path}" pulling_2_model: "${pulling_2_path}"
pulling_2_texture: "{pulling_2_path}" pulling_2_texture: "${pulling_2_path}"
# sounds # sounds
templates#settings#sounds: templates#settings#sounds:
default:sound/block_template: default:sound/block_template:
sounds: sounds:
break: "minecraft:block.{block_type}.break" break: "minecraft:block.${block_type}.break"
step: "minecraft:block.{block_type}.step" step: "minecraft:block.${block_type}.step"
place: "minecraft:block.{block_type}.place" place: "minecraft:block.${block_type}.place"
hit: "minecraft:block.{block_type}.hit" hit: "minecraft:block.${block_type}.hit"
fall: "minecraft:block.{block_type}.fall" fall: "minecraft:block.${block_type}.fall"
default:sound/crop: default:sound/crop:
sounds: sounds:
break: "minecraft:block.crop.break" break: "minecraft:block.crop.break"
@@ -614,7 +614,7 @@ templates#settings#break_level:
# block settings # block settings
templates#settings#blocks: templates#settings#blocks:
default:settings/middle_click_pick_itself: default:settings/middle_click_pick_itself:
item: "{__NAMESPACE__}:{__ID__}" item: "${__NAMESPACE__}:${__ID__}"
default:settings/solid_1x1x1: default:settings/solid_1x1x1:
is-suffocating: true is-suffocating: true
replaceable: false replaceable: false
@@ -696,7 +696,7 @@ templates#settings#blocks:
default:settings/ore: default:settings/ore:
template: template:
- "default:sound/stone" - "default:sound/stone"
- "default:pickaxe_power/level_{break_power}" - "default:pickaxe_power/level_${break_power}"
overrides: overrides:
hardness: 3.0 hardness: 3.0
resistance: 3.0 resistance: 3.0
@@ -712,7 +712,7 @@ templates#settings#blocks:
default:settings/deepslate_ore: default:settings/deepslate_ore:
template: template:
- "default:sound/deepslate" - "default:sound/deepslate"
- "default:pickaxe_power/level_{break_power}" - "default:pickaxe_power/level_${break_power}"
overrides: overrides:
hardness: 4.5 hardness: 4.5
resistance: 3.0 resistance: 3.0
@@ -735,45 +735,45 @@ templates#block_states:
default: y default: y
appearances: appearances:
axisY: axisY:
state: "{base_block}:{vanilla_id}" state: "${base_block}:${vanilla_id}"
model: model:
path: "{model_vertical_path}" path: "${model_vertical_path}"
generation: generation:
parent: "minecraft:block/cube_column" parent: "minecraft:block/cube_column"
textures: textures:
"end": "{texture_top_path}" "end": "${texture_top_path}"
"side": "{texture_side_path}" "side": "${texture_side_path}"
axisX: axisX:
state: "{base_block}:{vanilla_id}" state: "${base_block}:${vanilla_id}"
model: model:
x: 90 x: 90
y: 90 y: 90
path: "{model_horizontal_path}" path: "${model_horizontal_path}"
generation: generation:
parent: "minecraft:block/cube_column_horizontal" parent: "minecraft:block/cube_column_horizontal"
textures: textures:
"end": "{texture_top_path}" "end": "${texture_top_path}"
"side": "{texture_side_path}" "side": "${texture_side_path}"
axisZ: axisZ:
state: "{base_block}:{vanilla_id}" state: "${base_block}:${vanilla_id}"
model: model:
x: 90 x: 90
path: "{model_horizontal_path}" path: "${model_horizontal_path}"
generation: generation:
parent: "minecraft:block/cube_column_horizontal" parent: "minecraft:block/cube_column_horizontal"
textures: textures:
"end": "{texture_top_path}" "end": "${texture_top_path}"
"side": "{texture_side_path}" "side": "${texture_side_path}"
variants: variants:
axis=x: axis=x:
appearance: axisX appearance: axisX
id: "{internal_id}" id: "${internal_id}"
axis=y: axis=y:
appearance: axisY appearance: axisY
id: "{internal_id}" id: "${internal_id}"
axis=z: axis=z:
appearance: axisZ appearance: axisZ
id: "{internal_id}" id: "${internal_id}"
default:block_state/leaves: default:block_state/leaves:
properties: properties:
waterlogged: waterlogged:
@@ -788,107 +788,107 @@ templates#block_states:
range: 1~7 range: 1~7
appearances: appearances:
default: default:
state: "{default_state}" state: "${default_state}"
model: model:
path: "{model_path}" path: "${model_path}"
generation: generation:
parent: "minecraft:block/leaves" parent: "minecraft:block/leaves"
textures: textures:
"all": "{texture_path}" "all": "${texture_path}"
waterlogged: waterlogged:
state: "{waterlogged_state}" state: "${waterlogged_state}"
model: model:
path: "{model_path}" path: "${model_path}"
variants: variants:
distance=1,persistent=false,waterlogged=false: distance=1,persistent=false,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=2,persistent=false,waterlogged=false: distance=2,persistent=false,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=3,persistent=false,waterlogged=false: distance=3,persistent=false,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=4,persistent=false,waterlogged=false: distance=4,persistent=false,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=5,persistent=false,waterlogged=false: distance=5,persistent=false,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=6,persistent=false,waterlogged=false: distance=6,persistent=false,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=7,persistent=false,waterlogged=false: distance=7,persistent=false,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
is-randomly-ticking: true is-randomly-ticking: true
distance=1,persistent=true,waterlogged=false: distance=1,persistent=true,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=2,persistent=true,waterlogged=false: distance=2,persistent=true,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=3,persistent=true,waterlogged=false: distance=3,persistent=true,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=4,persistent=true,waterlogged=false: distance=4,persistent=true,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=5,persistent=true,waterlogged=false: distance=5,persistent=true,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=6,persistent=true,waterlogged=false: distance=6,persistent=true,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=7,persistent=true,waterlogged=false: distance=7,persistent=true,waterlogged=false:
appearance: "default" appearance: "default"
id: "{internal_id}" id: "${internal_id}"
distance=1,persistent=false,waterlogged=true: distance=1,persistent=false,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=2,persistent=false,waterlogged=true: distance=2,persistent=false,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=3,persistent=false,waterlogged=true: distance=3,persistent=false,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=4,persistent=false,waterlogged=true: distance=4,persistent=false,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=5,persistent=false,waterlogged=true: distance=5,persistent=false,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=6,persistent=false,waterlogged=true: distance=6,persistent=false,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=7,persistent=false,waterlogged=true: distance=7,persistent=false,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
@@ -896,49 +896,49 @@ templates#block_states:
fluid-state: water fluid-state: water
distance=1,persistent=true,waterlogged=true: distance=1,persistent=true,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=2,persistent=true,waterlogged=true: distance=2,persistent=true,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=3,persistent=true,waterlogged=true: distance=3,persistent=true,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=4,persistent=true,waterlogged=true: distance=4,persistent=true,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=5,persistent=true,waterlogged=true: distance=5,persistent=true,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=6,persistent=true,waterlogged=true: distance=6,persistent=true,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
fluid-state: water fluid-state: water
distance=7,persistent=true,waterlogged=true: distance=7,persistent=true,waterlogged=true:
appearance: "waterlogged" appearance: "waterlogged"
id: "{internal_id}" id: "${internal_id}"
settings: settings:
resistance: 1200.0 resistance: 1200.0
burnable: false burnable: false
@@ -951,9 +951,9 @@ templates#recipes:
category: building category: building
group: planks group: planks
ingredients: ingredients:
A: "#default:{wood_type}_logs" A: "#default:${wood_type}_logs"
result: result:
id: "default:{wood_type}_planks" id: "default:${wood_type}_planks"
count: 4 count: 4
default:recipe/log_2_wood: default:recipe/log_2_wood:
type: shaped type: shaped
@@ -963,29 +963,29 @@ templates#recipes:
- "AA" - "AA"
- "AA" - "AA"
ingredients: ingredients:
A: "default:{wood_type}_log" A: "default:${wood_type}_log"
result: result:
id: "default:{wood_type}_wood" id: "default:${wood_type}_wood"
count: 3 count: 3
default:recipe/smelting_ore: default:recipe/smelting_ore:
type: smelting type: smelting
experience: "{exp}" experience: "${exp}"
category: misc category: misc
group: topaz group: topaz
time: 200 time: 200
ingredient: "{ingredient}" ingredient: "${ingredient}"
result: result:
id: "{result}" id: "${result}"
count: 1 count: 1
default:recipe/blasting_ore: default:recipe/blasting_ore:
type: blasting type: blasting
experience: "{exp}" experience: "${exp}"
category: misc category: misc
group: topaz group: topaz
time: 100 time: 100
ingredient: "{ingredient}" ingredient: "${ingredient}"
result: result:
id: "{result}" id: "${result}"
count: 1 count: 1
# loot tables # loot tables
@@ -1001,7 +1001,7 @@ templates#loot_tables:
- type: survives_explosion - type: survives_explosion
entries: entries:
- type: item - type: item
item: "{__NAMESPACE__}:{__ID__}" item: "${__NAMESPACE__}:${__ID__}"
# drop one item # drop one item
@@ -1015,7 +1015,7 @@ templates#loot_tables:
- type: survives_explosion - type: survives_explosion
entries: entries:
- type: item - type: item
item: "{item}" item: "${item}"
# drop the original furniture item or a fallback item # drop the original furniture item or a fallback item
@@ -1027,7 +1027,7 @@ templates#loot_tables:
- rolls: 1 - rolls: 1
entries: entries:
- type: furniture_item - type: furniture_item
item: "{item}" item: "${item}"
# drop with silk touch # drop with silk touch
@@ -1042,7 +1042,7 @@ templates#loot_tables:
predicate: minecraft:silk_touch>=1 predicate: minecraft:silk_touch>=1
entries: entries:
- type: item - type: item
item: "{item}" item: "${item}"
# crop drops # crop drops
@@ -1058,21 +1058,21 @@ templates#loot_tables:
- type: alternatives - type: alternatives
children: children:
- type: item - type: item
item: "{crop_item}" item: "${crop_item}"
conditions: conditions:
- type: match_block_property - type: match_block_property
properties: properties:
age: "{ripe_age}" age: "${ripe_age}"
- type: item - type: item
item: "{crop_seed}" item: "${crop_seed}"
- rolls: 1 - rolls: 1
conditions: conditions:
- type: match_block_property - type: match_block_property
properties: properties:
age: "{ripe_age}" age: "${ripe_age}"
entries: entries:
- type: item - type: item
item: "{crop_seed}" item: "${crop_seed}"
functions: functions:
- type: apply_bonus - type: apply_bonus
enchantment: minecraft:fortune enchantment: minecraft:fortune
@@ -1090,15 +1090,15 @@ templates#loot_tables:
- rolls: 1 - rolls: 1
entries: entries:
- type: item - type: item
item: "{crop_item}" item: "${crop_item}"
- rolls: 1 - rolls: 1
conditions: conditions:
- type: match_block_property - type: match_block_property
properties: properties:
age: "{ripe_age}" age: "${ripe_age}"
entries: entries:
- type: item - type: item
item: "{crop_item}" item: "${crop_item}"
functions: functions:
- type: apply_bonus - type: apply_bonus
enchantment: minecraft:fortune enchantment: minecraft:fortune
@@ -1122,12 +1122,12 @@ templates#loot_tables:
- type: alternatives - type: alternatives
children: children:
- type: item - type: item
item: "{ore_block}" item: "${ore_block}"
conditions: conditions:
- type: enchantment - type: enchantment
predicate: minecraft:silk_touch>=1 predicate: minecraft:silk_touch>=1
- type: item - type: item
item: "{ore_drop}" item: "${ore_drop}"
functions: functions:
- type: apply_bonus - type: apply_bonus
enchantment: minecraft:fortune enchantment: minecraft:fortune
@@ -1137,8 +1137,8 @@ templates#loot_tables:
- type: drop_exp - type: drop_exp
count: count:
type: uniform type: uniform
min: "{min_exp}" min: "${min_exp:-2}"
max: "{max_exp}" max: "${max_exp:-4}"
# template: default:loot_table/ore_no_exp # template: default:loot_table/ore_no_exp
# arguments: # arguments:
@@ -1151,12 +1151,12 @@ templates#loot_tables:
- type: alternatives - type: alternatives
children: children:
- type: item - type: item
item: "{ore_block}" item: "${ore_block}"
conditions: conditions:
- type: enchantment - type: enchantment
predicate: minecraft:silk_touch>=1 predicate: minecraft:silk_touch>=1
- type: item - type: item
item: "{ore_drop}" item: "${ore_drop}"
functions: functions:
- type: apply_bonus - type: apply_bonus
enchantment: minecraft:fortune enchantment: minecraft:fortune
@@ -1177,7 +1177,7 @@ templates#loot_tables:
- type: alternatives - type: alternatives
children: children:
- type: item - type: item
item: "{leaves}" item: "${leaves}"
conditions: conditions:
- type: any_of - type: any_of
terms: terms:
@@ -1186,7 +1186,7 @@ templates#loot_tables:
- type: enchantment - type: enchantment
predicate: minecraft:silk_touch>=1 predicate: minecraft:silk_touch>=1
- type: item - type: item
item: "{sapling}" item: "${sapling}"
conditions: conditions:
- type: survives_explosion - type: survives_explosion
- type: table_bonus - type: table_bonus

View File

@@ -69,21 +69,21 @@ images:
templates: templates:
internal:icon/2d: internal:icon/2d:
material: arrow material: arrow
custom-model-data: "{model_data}" custom-model-data: "${model_data}"
data: data:
item-name: "{name}" item-name: "${name}"
lore: "{lore}" lore: "${lore}"
model: model:
template: "internal:model/simplified_generated" template: "internal:model/simplified_generated"
arguments: arguments:
path: "minecraft:item/custom/gui/{texture}" path: "minecraft:item/custom/gui/${texture}"
internal:model/simplified_generated: internal:model/simplified_generated:
type: "minecraft:model" type: "minecraft:model"
path: "{path}" path: "${path}"
generation: generation:
parent: "minecraft:item/generated" parent: "minecraft:item/generated"
textures: textures:
"layer0": "{path}" "layer0": "${path}"
items: items:
internal:next_page_0: internal:next_page_0:

View File

@@ -64,6 +64,7 @@ command.upload.on_progress: "<white>Started uploading progress. Check the consol
command.send_resource_pack.success.single: "<white>Sent resource pack to <arg:0>.</white>" command.send_resource_pack.success.single: "<white>Sent resource pack to <arg:0>.</white>"
command.send_resource_pack.success.multiple: "<white>Send resource packs to <arg:0> players.</white>" command.send_resource_pack.success.multiple: "<white>Send resource packs to <arg:0> players.</white>"
warning.config.pack.duplicated_files: "</red>Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section.</red>" warning.config.pack.duplicated_files: "</red>Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section.</red>"
warning.config.yaml.duplicated_key: "<red>Issue found in file <arg:0> - Found duplicated key '<arg:1>' at line <arg:2>, this might cause unexpected results.</red>"
warning.config.type.int: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to integer type for option '<arg:3>'.</yellow>" warning.config.type.int: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to integer type for option '<arg:3>'.</yellow>"
warning.config.type.float: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to float type for option '<arg:3>'.</yellow>" warning.config.type.float: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to float type for option '<arg:3>'.</yellow>"
warning.config.type.double: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to double type for option '<arg:3>'.</yellow>" warning.config.type.double: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to double type for option '<arg:3>'.</yellow>"
@@ -113,7 +114,6 @@ warning.config.image.missing_char: "<yellow>Issue found in file <arg:0> - The im
warning.config.image.codepoint_conflict: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a character '<arg:3>(<arg:4>)' in font <arg:2> that has been used by another image '<arg:5>'.</yellow>" warning.config.image.codepoint_conflict: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a character '<arg:3>(<arg:4>)' in font <arg:2> that has been used by another image '<arg:5>'.</yellow>"
warning.config.image.invalid_codepoint_grid: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has an invalid 'chars' codepoint grid.</yellow>" warning.config.image.invalid_codepoint_grid: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has an invalid 'chars' codepoint grid.</yellow>"
warning.config.image.invalid_char: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has a char parameter containing combining characters, which may result in image splitting.</yellow>" warning.config.image.invalid_char: "<yellow>Issue found in file <arg:0> - Image '<arg:1>' has a char parameter containing combining characters, which may result in image splitting.</yellow>"
warning.config.image.file_not_found: "<yellow>Issue found in file <arg:0> - PNG file '<arg:2>' not found for image '<arg:1>'.</yellow>"
warning.config.image.invalid_hex_value: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a unicode character '<arg:2>' that is not a valid hexadecimal (radix 16) value.</yellow>" warning.config.image.invalid_hex_value: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is using a unicode character '<arg:2>' that is not a valid hexadecimal (radix 16) value.</yellow>"
warning.config.recipe.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated recipe '<arg:1>'. Please check if there is the same configuration in other files.</yellow>" warning.config.recipe.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated recipe '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.recipe.missing_type: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is missing the required 'type' argument.</yellow>" warning.config.recipe.missing_type: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is missing the required 'type' argument.</yellow>"
@@ -133,8 +133,10 @@ warning.config.recipe.smithing_transform.post_processor.keep_component.missing_c
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'tags' for post-processors 'keep_tags'.</yellow>" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'tags' for post-processors 'keep_tags'.</yellow>"
warning.config.i18n.unknown_locale: "<yellow>Issue found in file <arg:0> - Unknown locale '<arg:1>'.</yellow>" warning.config.i18n.unknown_locale: "<yellow>Issue found in file <arg:0> - Unknown locale '<arg:1>'.</yellow>"
warning.config.template.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated template '<arg:1>'. Please check if there is the same configuration in other files.</yellow>" warning.config.template.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated template '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.template.invalid: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid template '<arg:2>'.</yellow>"
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'from' '<arg:2>' larger than 'to' '<arg:3>' in 'self_increase_int' argument.</yellow>" warning.config.template.argument.self_increase_int.invalid_range: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'from' '<arg:2>' larger than 'to' '<arg:3>' in 'self_increase_int' argument.</yellow>"
warning.config.template.argument.list.invalid_type: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) '<arg:2>'.</yellow>" warning.config.template.argument.list.invalid_type: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) '<arg:2>'.</yellow>"
warning.config.template.argument.default_value.invalid_syntax: "<yellow>Issue found in file <arg:0> - The template '<arg:1>' is using an invalid default value '<arg:2>' for argument '<arg:3>'.</yellow>"
warning.config.vanilla_loot.missing_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is missing the required 'type' argument.</yellow>" warning.config.vanilla_loot.missing_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is missing the required 'type' argument.</yellow>"
warning.config.vanilla_loot.invalid_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is using an invalid type '<arg:2>'. Allowed types: [<arg:3>].</yellow>" warning.config.vanilla_loot.invalid_type: "<yellow>Issue found in file <arg:0> - The vanilla loot '<arg:1>' is using an invalid type '<arg:2>'. Allowed types: [<arg:3>].</yellow>"
warning.config.vanilla_loot.block.invalid_target: "<yellow>Issue found in file <arg:0> - Invalid block target '<arg:2>' in vanilla loot '<arg:1>'.</yellow>" warning.config.vanilla_loot.block.invalid_target: "<yellow>Issue found in file <arg:0> - Invalid block target '<arg:2>' in vanilla loot '<arg:1>'.</yellow>"
@@ -151,11 +153,13 @@ warning.config.furniture.hitbox.invalid_type: "<yellow>Issue found in file <arg:
warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is using a custom hitbox with invalid entity type '<arg:2>'.</yellow>" warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>Issue found in file <arg:0> - The furniture '<arg:1>' is using a custom hitbox with invalid entity type '<arg:2>'.</yellow>"
warning.config.item.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated item '<arg:1>'. Please check if there is the same configuration in other files.</yellow>" warning.config.item.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated item '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.item.settings.unknown: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an unknown setting type '<arg:2>'.</yellow>" warning.config.item.settings.unknown: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an unknown setting type '<arg:2>'.</yellow>"
warning.config.item.settings.invulnerable.invalid_damage_source: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an unknown damage source '<arg:2>'. Allowed sources: [<arg:3>].</yellow>"
warning.config.item.missing_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'material' argument.</yellow>" warning.config.item.missing_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'material' argument.</yellow>"
warning.config.item.invalid_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid material type '<arg:2>'.</yellow>" warning.config.item.invalid_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid material type '<arg:2>'.</yellow>"
warning.config.item.invalid_custom_model_data: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a negative custom model data '<arg:2>' which is invalid.</yellow>" warning.config.item.invalid_custom_model_data: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a negative custom model data '<arg:2>' which is invalid.</yellow>"
warning.config.item.bad_custom_model_data: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data '<arg:2>' that is too large. It's recommended to use a value lower than 16,777,216.</yellow>" warning.config.item.bad_custom_model_data: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data '<arg:2>' that is too large. It's recommended to use a value lower than 16,777,216.</yellow>"
warning.config.item.custom_model_data_conflict: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data '<arg:2>' that has been occupied by item '<arg:3>'.</yellow>" warning.config.item.custom_model_data_conflict: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a custom model data '<arg:2>' that has been occupied by item '<arg:3>'.</yellow>"
warning.config.item.invalid_component: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a non-existing component type '<arg:2>'.</yellow>"
warning.config.item.missing_model_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'custom-model-data' or 'item-model' argument.</yellow>" warning.config.item.missing_model_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'custom-model-data' or 'item-model' argument.</yellow>"
warning.config.item.missing_model: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'model' section for 1.21.4+ resource pack support.</yellow>" warning.config.item.missing_model: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'model' section for 1.21.4+ resource pack support.</yellow>"
warning.config.item.behavior.missing_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'type' argument for its item behavior.</yellow>" warning.config.item.behavior.missing_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'type' argument for its item behavior.</yellow>"
@@ -350,3 +354,8 @@ warning.config.selector.invalid_target: "<yellow>Issue found in file <arg:0> - T
warning.config.resource_pack.item_model.conflict.vanilla: "<yellow>Failed to generate item model for '<arg:0>' because this item model has been occupied by a vanilla item.</yellow>" warning.config.resource_pack.item_model.conflict.vanilla: "<yellow>Failed to generate item model for '<arg:0>' because this item model has been occupied by a vanilla item.</yellow>"
warning.config.resource_pack.item_model.already_exist: "<yellow>Failed to generate item model for '<arg:0>' because the file '<arg:1>' already exists.</yellow>" warning.config.resource_pack.item_model.already_exist: "<yellow>Failed to generate item model for '<arg:0>' because the file '<arg:1>' already exists.</yellow>"
warning.config.resource_pack.model.generation.already_exist: "<yellow>Failed to generate model because the model file '<arg:0>' already exists.</yellow>" warning.config.resource_pack.model.generation.already_exist: "<yellow>Failed to generate model because the model file '<arg:0>' already exists.</yellow>"
warning.config.resource_pack.generation.missing_font_texture: "<yellow>Font '<arg:0>' is missing required texture: '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_model_texture: "<yellow>Model '<arg:0>' is missing texture '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_item_model: "<yellow>Item '<arg:0>' is missing model file: '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_block_model: "<yellow>Block '<arg:0>' is missing model file: '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_parent_model: "<yellow>Model '<arg:0>' cannot find parent model: '<arg:1>'</yellow>"

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