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

Merge pull request #220 from Xiao-MoMi/main

更新上游
This commit is contained in:
XiaoMoMi
2025-06-13 04:44:50 +08:00
committed by GitHub
189 changed files with 4269 additions and 1699 deletions

View File

@@ -6,14 +6,11 @@
</h1>
<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">
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
</a>
<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>
</p>
@@ -55,7 +52,7 @@ The code you contribute will be open-sourced under the GPLv3 license. If you pre
### 🌍 Translations
1. Clone this repository.
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
| Version | Official Support | Max Players | Dev Builds |
@@ -79,7 +76,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.56")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.56")
compileOnly("net.momirealms:craft-engine-core:0.0.57")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.57")
}
```

View File

@@ -27,7 +27,7 @@ subprojects {
expand(rootProject.properties)
}
filesMatching(arrayListOf("commands.yml", "config.yml", "*/*.yml", "ignite.mod.json")) {
filesMatching(arrayListOf("commands.yml", "config.yml")) {
expand(
Pair("project_version", rootProject.properties["project_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.infernalsuite.com/repository/maven-snapshots/") // slime world
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://repo.viaversion.com") // via
maven("https://repo.skriptlang.org/releases/") // skript
@@ -47,6 +47,8 @@ dependencies {
compileOnly(platform("com.intellectualsites.bom:bom-newest:1.52"))
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core")
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false }
// MythicMobs
compileOnly("io.lumine:Mythic-Dist:5.9.0")
}
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.compatibility.bettermodel.BetterModelModel;
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.legacy.slimeworld.LegacySlimeFormatStorageAdaptor;
import net.momirealms.craftengine.bukkit.compatibility.leveler.AuraSkillsLevelerProvider;
import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineModel;
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.permission.LuckPermsEventListeners;
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
@@ -104,6 +106,12 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
}
if (this.isPluginEnabled("AuraSkills")) {
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;
if (param.length == 4) {
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) {
codepoint = image.codepointAt(0,0);
} else {

View File

@@ -7,14 +7,13 @@ import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.util.Kleenean;
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.util.Key;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class ExprCustomItem extends SimpleExpression<ItemStack> {
public static void register() {
@@ -36,7 +35,8 @@ public class ExprCustomItem extends SimpleExpression<ItemStack> {
String itemId = this.itemId.getSingle(e);
if (itemId == 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

View File

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

View File

@@ -71,27 +71,47 @@ paper {
required = false
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
register("NeigeItems") {
required = false
}
register("MMOItems") {
required = false
}
register("ModelEngine") {
required = false
}
register("BetterModel") {
required = false
}
register("AuraSkills") {
required = false
}
register("LuckPerms") {
required = false
}
register("ViaVersion") {
required = false
}
register("LuckPerms") { required = false }
register("ViaVersion") { required = false }
// external models
register("ModelEngine") { required = false }
register("BetterModel") { required = false }
// external items
register("NeigeItems") { required = false }
register("MMOItems") { required = false }
register("MythicMobs") { required = false }
// leveler
register("AuraSkills") { required = false }
// anti grief lib
register("Dominion") { required = false }
register("WorldGuard") { required = false }
register("Kingdoms") { 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();
} else {
try {
logger.info("Patching the server...");
RuntimePatcher.patch(this.plugin);
} catch (Exception 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) {
CustomFurniture furniture = byId(furnitureId);
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 org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -371,19 +370,18 @@ public class BlockEventListener implements Listener {
// for vanilla blocks
if (event.getChangedType() == Material.NOTE_BLOCK) {
Block block = event.getBlock();
World world = block.getWorld();
Location location = block.getLocation();
Block sourceBlock = event.getSourceBlock();
BlockFace direction = sourceBlock.getFace(block);
if (direction == BlockFace.UP || direction == BlockFace.DOWN) {
if (block.getX() == sourceBlock.getX() && block.getX() == sourceBlock.getZ()) {
World world = block.getWorld();
Location location = block.getLocation();
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world);
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
Object blockPos = LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
if (direction == BlockFace.UP) {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, 0);
if (block.getY() > sourceBlock.getY()) {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, Config.maxNoteBlockChainUpdate());
} 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.MBuiltInRegistries;
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.KeyUtils;
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.ResourceLocation;
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.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
@@ -45,7 +45,6 @@ import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -93,10 +92,8 @@ public class BukkitBlockManager extends AbstractBlockManager {
this.blockParser = new BlockParser();
this.initVanillaRegistry();
this.loadMappingsAndAdditionalBlocks();
if (!plugin.requiresRestart()) {
this.registerBlocks();
this.registerEmptyBlock();
}
this.registerBlocks();
this.registerEmptyBlock();
}
@Override
@@ -163,9 +160,9 @@ public class BukkitBlockManager extends AbstractBlockManager {
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
}
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.instance$Registries$BLOCK, list));
for (Player player : Bukkit.getOnlinePlayers()) {
this.plugin.networkManager().sendPacket(this.plugin.adapt(player), packet);
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, list));
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
player.sendPacket(packet, false);
}
// 如果空,那么新来的玩家就没必要收到更新包了
if (list.isEmpty()) {
@@ -276,19 +273,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
private void initVanillaRegistry() {
int vanillaStateCount;
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();
}
int vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
this.plugin.logger().info("Vanilla block count: " + vanillaStateCount);
BlockStateUtils.init(vanillaStateCount);
}
@@ -350,7 +335,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
public class BlockParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"blocks", "block"};
public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"};
@Override
public String[] sectionId() {
@@ -512,10 +497,13 @@ public class BukkitBlockManager extends AbstractBlockManager {
throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath);
}
json.addProperty("model", modelPath);
if (singleModelMap.containsKey("x")) 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("x"))
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("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);
if (generationMap != null) {
prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap));
@@ -591,6 +579,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
Object resourceLocation = KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK);
Object block = CoreReflections.method$Registry$get.invoke(MBuiltInRegistries.BLOCK, resourceLocation);
Object stateDefinition = CoreReflections.field$Block$StateDefinition.get(block);
@SuppressWarnings("unchecked")
ImmutableList<Object> states = (ImmutableList<Object>) CoreReflections.field$StateDefinition$states.get(stateDefinition);
for (Object state : states) {
BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(state, new Object());
@@ -723,29 +712,20 @@ public class BukkitBlockManager extends AbstractBlockManager {
Object blockHolder;
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 {
newRealBlock = BlockGenerator.generateBlock(clientSideBlockType, clientSideBlock, blockProperties);
} catch (Throwable throwable) {
this.plugin.logger().warn("Failed to generate dynamic block class", throwable);
continue;
}
blockHolder = CoreReflections.method$Registry$registerForHolder.invoke(null, MBuiltInRegistries.BLOCK, resourceLocation, newRealBlock);
CoreReflections.method$Holder$Reference$bindValue.invoke(blockHolder, newRealBlock);
CoreReflections.field$Holder$Reference$tags.set(blockHolder, Set.of());
newBlockState = getOnlyBlockState(newRealBlock);
CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState);
try {
newRealBlock = BlockGenerator.generateBlock(clientSideBlockType, clientSideBlock, blockProperties);
} catch (Throwable throwable) {
this.plugin.logger().warn("Failed to generate dynamic block class", throwable);
continue;
}
blockHolder = CoreReflections.method$Registry$registerForHolder.invoke(null, MBuiltInRegistries.BLOCK, resourceLocation, newRealBlock);
CoreReflections.method$Holder$Reference$bindValue.invoke(blockHolder, newRealBlock);
CoreReflections.field$Holder$Reference$tags.set(blockHolder, Set.of());
newBlockState = getOnlyBlockState(newRealBlock);
CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState);
if (isNoteBlock) {
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 {
Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null);
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) {
CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey);
}

View File

@@ -129,13 +129,12 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
// init cache
CoreReflections.method$BlockStateBase$initCache.invoke(mcBlockState);
// set block light
if (settings.blockLight() != -1) {
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.field$BlockStateBase$lightBlock.set(mcBlockState, settings.blockLight());
} else {
Object cache = CoreReflections.field$BlockStateBase$cache.get(mcBlockState);
CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, settings.blockLight());
}
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(state.vanillaBlockState().handle());
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.field$BlockStateBase$lightBlock.set(mcBlockState, blockLight);
} else {
Object cache = CoreReflections.field$BlockStateBase$cache.get(mcBlockState);
CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight);
}
// set fluid later
if (settings.fluidState()) {
@@ -151,7 +150,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId());
Set<Object> tags = new HashSet<>();
for (Key tag : settings.tags()) {
tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.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);
// 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 {
Object world = args[1];
Object blockPos = args[2];
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2);
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
}
@Override
@@ -86,7 +86,7 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio
return state;
}
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;
}
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 {
Object world = args[1];
Object blockPos = args[2];
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2);
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
}
@Override
@@ -50,7 +50,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
world = args[3];
blockPos = args[4];
}
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2);
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
return args[0];
}

View File

@@ -62,7 +62,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
boolean lit = state.get(this.litProperty);
if (lit != FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
if (lit) {
CoreReflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 4);
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 4);
} else {
// TODO Call Event
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();
int distance = behavior.getDistanceAt(neighborState) + 1;
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 {
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;
@SuppressWarnings("unchecked")
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();
int id();
T defaultValue();
Object entityDataAccessor();
default Object createEntityDataIfNotDefaultValue(T value) {
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) {
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) {
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) {

View File

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

View File

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

View File

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

View File

@@ -49,7 +49,7 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
Vector3f offset = conjugated.transform(new Vector3f(position()));
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
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)));
}

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_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION);
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;
private static BukkitFurnitureManager instance;
private final BukkitCraftEngine plugin;
@@ -70,7 +70,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
Optional<AnchorType> optionalAnchorType = extraData.anchorType();
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 -> {
ItemDisplay display = (ItemDisplay) entity;
@@ -92,7 +92,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
@Override
public void delayedInit() {
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();
Bukkit.getPluginManager().registerEvents(this.dismountListener, 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();
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
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);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
if (canUseItemOn()) {

View File

@@ -61,7 +61,7 @@ public class ShulkerHitBox extends AbstractHitBox {
if (interactionEntity) {
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
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);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) {
@@ -80,7 +80,7 @@ public class ShulkerHitBox extends AbstractHitBox {
if (interactionEntity) {
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,
MEntityTypes.instance$EntityType$INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) {
@@ -102,14 +102,14 @@ public class ShulkerHitBox extends AbstractHitBox {
// first interaction
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
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);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
// second interaction
double distance = shulkerHeight - scale;
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,
MEntityTypes.instance$EntityType$INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) {
@@ -213,11 +213,11 @@ public class ShulkerHitBox extends AbstractHitBox {
double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY;
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
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);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
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);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(this.cachedShulkerValues)), false);
// add passengers

View File

@@ -11,4 +11,9 @@ public class BukkitCustomProjectile extends AbstractCustomProjectile {
public BukkitCustomProjectile(ProjectileMeta meta, Projectile projectile, Item<ItemStack> 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 {
private static BukkitProjectileManager instance;
private final BukkitCraftEngine plugin;
private final Map<Integer, BukkitCustomProjectile> projectiles;
// 会被netty线程访问
private final Map<Integer, BukkitCustomProjectile> projectiles = new ConcurrentHashMap<>();
public BukkitProjectileManager(BukkitCraftEngine plugin) {
this.plugin = plugin;
this.projectiles = new ConcurrentHashMap<>();
instance = this;
}
@@ -114,7 +114,8 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
wrapped.getCustomItem().ifPresent(it -> {
ProjectileMeta meta = it.settings().projectileMeta();
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);
}
});
@@ -161,7 +162,8 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
public class ProjectileInjectTask implements Runnable {
private final Projectile projectile;
private final SchedulerTask task;
private boolean injected;
private Object cachedServerEntity;
private int lastInjectedInterval = 0;
public ProjectileInjectTask(Projectile projectile) {
this.projectile = projectile;
@@ -178,32 +180,42 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
this.task.cancel();
return;
}
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);
if (trackedEntity == null) {
return;
}
Object serverEntity = FastNMS.INSTANCE.filed$ChunkMap$TrackedEntity$serverEntity(trackedEntity);
if (serverEntity == null) {
return;
}
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 (trackedEntity == null) return;
Object serverEntity = FastNMS.INSTANCE.field$ChunkMap$TrackedEntity$serverEntity(trackedEntity);
if (serverEntity == null) return;
this.cachedServerEntity = serverEntity;
}
if (canSpawnParticle(nmsEntity)) {
boolean inGround = FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity);
if (canSpawnParticle(nmsEntity, inGround)) {
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 (CoreReflections.clazz$AbstractArrow.isInstance(nmsEntity)) {
return !FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity);
return !inGround;
}
return true;
}

View File

@@ -1,7 +1,7 @@
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.util.KeyUtils;
import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
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.registry.Holder;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
@@ -19,22 +18,23 @@ import java.util.List;
import java.util.Map;
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<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers,
ItemSettings settings,
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
super(id, materialKey, behaviors, modifiers, clientBoundModifiers, settings, events);
this.material = material;
super(id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events);
this.item = item;
this.clientItem = clientItem;
}
@Override
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);
wrapped.count(count);
for (ItemDataModifier<ItemStack> modifier : this.modifiers) {
modifier.apply(wrapped, context);
}
@@ -43,7 +43,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
@Override
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);
for (ItemDataModifier<ItemStack> modifier : dataModifiers()) {
modifier.apply(wrapped, context);
@@ -51,23 +51,33 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
return BukkitCraftEngine.instance().itemManager().wrap(wrapped.load());
}
public static Builder<ItemStack> builder(Material material) {
return new BuilderImpl(material);
public Object clientItem() {
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> {
private Holder<Key> id;
private Key materialKey;
private final Material material;
private Key itemKey;
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 List<ItemBehavior> behaviors = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4);
private ItemSettings settings;
public BuilderImpl(Material material) {
this.material = material;
this.materialKey = KeyUtils.namespacedKey2Key(material.getKey());
public BuilderImpl(Object item, Object clientBoundItem) {
this.item = item;
this.clientBoundItem = clientBoundItem;
}
@Override
@@ -76,9 +86,15 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
return this;
}
@Override
public Builder<ItemStack> clientBoundMaterial(Key clientBoundMaterial) {
this.clientBoundItemKey = clientBoundMaterial;
return this;
}
@Override
public Builder<ItemStack> material(Key material) {
this.materialKey = material;
this.itemKey = material;
return this;
}
@@ -133,7 +149,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
@Override
public CustomItem<ItemStack> build() {
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);
}
}

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item;
import com.saicone.rtag.item.ItemTagStream;
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.factory.BukkitItemFactory;
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.MBuiltInRegistries;
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.KeyUtils;
import net.momirealms.craftengine.core.entity.player.Player;
@@ -43,6 +45,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
static {
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
registerVanillaItemExtraBehavior(FlintAndSteelItemBehavior.INSTANCE, ItemKeys.FLINT_AND_STEEL);
}
private static BukkitItemManager instance;
@@ -198,9 +201,19 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
}
@Override
protected CustomItem.Builder<ItemStack> createPlatformItemBuilder(Holder<Key> id, Key materialId) {
Material material = ResourceConfigUtils.requireNonNullOrThrow(Registry.MATERIAL.get(KeyUtils.toNamespacedKey(materialId)), () -> new LocalizedResourceConfigException("warning.config.item.invalid_material", materialId.toString()));
return BukkitCustomItem.builder(material).material(materialId).id(id);
protected CustomItem.Builder<ItemStack> createPlatformItemBuilder(Holder<Key> id, Key materialId, Key clientBoundMaterialId) {
Object item = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(materialId));
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")
@@ -214,7 +227,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
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);
for (Object tag : tags) {
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) {
if (value == null) return;
Object componentType = ensureDataComponentType(type);
if (componentType == null) {
return;
}
Codec codec = FastNMS.INSTANCE.method$DataComponentType$codec(componentType);
try {
DataResult<Object> result = codec.parse(ops, value);
@@ -162,4 +165,9 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
public void count(int amount) {
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> {
private final RtagItem rtagItem;
private int count;
public LegacyItemWrapper(RtagItem rtagItem, int count) {
public LegacyItemWrapper(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) {
@@ -49,12 +40,12 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
}
public int count() {
return this.count;
return getItem().getAmount();
}
public void count(int amount) {
if (amount < 0) amount = 0;
this.count = amount;
getItem().setAmount(amount);
}
public Object getExactTag(Object... path) {
@@ -75,9 +66,12 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
@Override
public ItemStack load() {
ItemStack itemStack = this.rtagItem.load();
itemStack.setAmount(Math.max(this.count, 0));
return itemStack;
return this.rtagItem.load();
}
@Override
public ItemStack getItem() {
return this.rtagItem.getItem();
}
@Override
@@ -87,6 +81,13 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
@Override
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;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
@@ -25,11 +26,25 @@ import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
@SuppressWarnings("DuplicatedCode")
public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
@Override
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);
if (networkData == null) return Optional.empty();
wrapped.removeTag(NETWORK_ITEM_TAG);
@@ -46,12 +61,16 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped).process();
return new OtherItem(wrapped, false).process();
} 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 (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped).process();
if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
return new OtherItem(wrapped, hasDifferentMaterial).process();
} else {
CompoundTag tag = new CompoundTag();
Tag argumentTag = wrapped.getNBTTag(ArgumentModifier.ARGUMENTS_TAG);
@@ -77,7 +96,12 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
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);
return Optional.of(wrapped);
}
@@ -130,9 +154,11 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
private final Item<ItemStack> item;
private boolean globalChanged = false;
private CompoundTag networkTag;
private final boolean forceReturn;
public OtherItem(Item<ItemStack> item) {
public OtherItem(Item<ItemStack> item, boolean forceReturn) {
this.item = item;
this.forceReturn = forceReturn;
}
public Optional<Item<ItemStack>> process() {
@@ -145,6 +171,8 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
if (this.globalChanged) {
this.item.setTag(this.networkTag, NETWORK_ITEM_TAG);
return Optional.of(this.item);
} else if (this.forceReturn) {
return Optional.of(this.item);
} else {
return Optional.empty();
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.item;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.*;
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) {
Tag customData = wrapped.getNBTComponent(ComponentTypes.CUSTOM_DATA);
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);
if (networkData == null) return Optional.empty();
if (networkData == null) {
if (hasDifferentMaterial) {
return Optional.of(wrapped);
}
return Optional.empty();
}
compoundTag.remove(NETWORK_ITEM_TAG);
for (Map.Entry<String, Tag> entry : networkData.entrySet()) {
if (entry.getValue() instanceof CompoundTag tag) {
@@ -48,12 +63,16 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped).process();
return new OtherItem(wrapped, false).process();
} 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 (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped).process();
if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
return new OtherItem(wrapped, hasDifferentMaterial).process();
} else {
CompoundTag customData = Optional.ofNullable(wrapped.getNBTComponent(ComponentTypes.CUSTOM_DATA)).map(CompoundTag.class::cast).orElse(new CompoundTag());
CompoundTag arguments = customData.getCompound(ArgumentModifier.ARGUMENTS_TAG);
@@ -86,7 +105,10 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
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);
wrapped.setNBTComponent(ComponentTypes.CUSTOM_DATA, customData);
return Optional.of(wrapped);
@@ -203,11 +225,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
static class OtherItem {
private final Item<ItemStack> item;
private final boolean forceReturn;
private boolean globalChanged = false;
private CompoundTag tag;
public OtherItem(Item<ItemStack> item) {
public OtherItem(Item<ItemStack> item, boolean forceReturn) {
this.item = item;
this.forceReturn = forceReturn;
}
public Optional<Item<ItemStack>> process() {
@@ -231,6 +255,8 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
customData.put(NETWORK_ITEM_TAG, getOrCreateTag());
this.item.setNBTComponent(ComponentKeys.CUSTOM_DATA, customData);
return Optional.of(this.item);
} else if (this.forceReturn) {
return Optional.of(this.item);
} else {
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 WATER_BUCKET_ITEM = Key.from("craftengine:water_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() {
register(EMPTY, EmptyItemBehavior.FACTORY);
@@ -19,5 +20,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.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.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.util.ItemTags;
import net.momirealms.craftengine.core.item.EquipmentData;
import net.momirealms.craftengine.core.item.ItemFactory;
import net.momirealms.craftengine.core.item.ItemWrapper;
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.util.Key;
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.ComponentTypes;
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.KeyUtils;
import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.Trim;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.inventory.ItemStack;
@@ -38,29 +43,136 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
return new ComponentItemWrapper(item);
}
@SuppressWarnings("unchecked")
@Override
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
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
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
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
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
@@ -397,9 +509,9 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
protected ComponentItemWrapper mergeCopy(ComponentItemWrapper item1, ComponentItemWrapper item2) {
Object itemStack1 = item1.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));
return new ComponentItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack3), item2.count());
return new ComponentItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack3));
}
@Override
@@ -409,7 +521,21 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
try {
FastNMS.INSTANCE.method$ItemStack$applyComponents(itemStack1, FastNMS.INSTANCE.method$ItemStack$getComponentsPatch(itemStack2));
} 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.ComponentTypes;
import net.momirealms.craftengine.core.item.EquipmentData;
import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.plugin.CraftEngine;
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.TagList;
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.Trim;
import net.momirealms.craftengine.core.item.modifier.IdModifier;
@@ -29,7 +32,7 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
@Override
protected LegacyItemWrapper wrapInternal(ItemStack item) {
return new LegacyItemWrapper(new RtagItem(item), item.getAmount());
return new LegacyItemWrapper(new RtagItem(item));
}
@Override
@@ -305,18 +308,32 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
@Override
protected LegacyItemWrapper mergeCopy(LegacyItemWrapper item1, LegacyItemWrapper item2) {
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
TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
return new LegacyItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count());
TagCompound.merge(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(itemStack), FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item2.getLiteralObject()), true, true);
return new LegacyItemWrapper(new RtagItem(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)));
}
@Override
protected void merge(LegacyItemWrapper item1, LegacyItemWrapper item2) {
// load previous changes on nms items
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
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);
player.sendPacket(systemChatPacket, false);
} 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");
if (storedData == null) storedData = new HashMap<>();
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.behavior.ItemBehavior;
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.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
@@ -28,11 +28,16 @@ import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
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.PlayerItemConsumeEvent;
import org.bukkit.inventory.EquipmentSlot;
@@ -49,6 +54,28 @@ public class ItemEventListener implements Listener {
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)
public void onInteractBlock(PlayerInteractEvent event) {
Action action = event.getAction();
@@ -62,6 +89,7 @@ public class ItemEventListener implements Listener {
}
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
if (serverPlayer == null) return;
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
// 如果本tick内主手已被处理则不处理副手
// 这是因为客户端可能会同时发主副手交互包,但实际上只能处理其中一个
@@ -318,18 +346,47 @@ public class ItemEventListener implements Listener {
if (optionalCustomItem.isEmpty()) {
return;
}
Cancellable dummy = Cancellable.dummy();
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
PlayerOptionalContext context = PlayerOptionalContext.of(this.plugin.adapt(event.getPlayer()), ContextHolder.builder()
.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)
);
customItem.execute(context, EventTrigger.CONSUME);
if (dummy.isCancelled()) {
event.setCancelled(true);
if (event.isCancelled()) {
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) {
@@ -344,4 +401,17 @@ public class ItemEventListener implements Listener {
}
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.reflection.bukkit.CraftBukkitReflections;
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.MaterialUtils;
import net.momirealms.craftengine.bukkit.util.RecipeUtils;
@@ -45,9 +46,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// 将自定义配方转为“广义”配方,接受更加宽容的输入
// 部分过程借助bukkit完成部分直接通过nms方法注册
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 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) {
try {
@@ -265,7 +266,8 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
}
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() {
@@ -287,6 +289,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
@Override
public void load() {
if (!Config.enableRecipeSystem()) return;
super.isReloading = true;
if (VersionHelper.isOrAbove1_21_2()) {
try {
this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager);
@@ -308,7 +311,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to unregister recipes", e);
}
recipeToMcRecipeHolder.clear();
}
@Override
@@ -320,6 +322,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
@Override
public void disable() {
unload();
CE_RECIPE_2_NMS_HOLDER.clear();
HandlerList.unregisterAll(this.recipeEventListener);
if (this.crafterEventListener != null) {
HandlerList.unregisterAll(this.crafterEventListener);
@@ -473,6 +476,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// clear cache
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) {
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);
}
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<>();
for (Holder<Key> holder : holders) {
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)
.toList();
Object shapedRecipe = getNMSRecipe(id);
recipeToMcRecipeHolder.put(recipe, shapedRecipe);
Object shapedRecipe = getOptionalNMSRecipe(id).get();
if (VersionHelper.isOrAbove1_20_2()) {
shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe);
}
@@ -731,8 +742,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
try {
List<Ingredient<ItemStack>> actualIngredients = recipe.ingredientsInUse();
Object shapelessRecipe = getNMSRecipe(id);
recipeToMcRecipeHolder.put(recipe, shapelessRecipe);
Object shapelessRecipe = getOptionalNMSRecipe(id).get();
if (VersionHelper.isOrAbove1_20_2()) {
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) {
try {
Ingredient<ItemStack> actualIngredient = recipe.ingredient();
Object smeltingRecipe = getNMSRecipe(id);
recipeToMcRecipeHolder.put(recipe, smeltingRecipe);
Object smeltingRecipe = getOptionalNMSRecipe(id).get();
if (VersionHelper.isOrAbove1_20_2()) {
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
// 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()) {
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")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey);
if (optional.isEmpty()) {
throw new IllegalArgumentException("Recipe " + id + " not found");
}
return optional.get();
return optional;
} else {
Object resourceLocation = KeyUtils.toResourceLocation(id);
@SuppressWarnings("unchecked")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation);
if (optional.isEmpty()) {
throw new IllegalArgumentException("Recipe " + id + " not found");
}
return optional.get();
return optional;
}
}

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.SingleItemInput;
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.context.ContextHolder;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
@@ -32,6 +33,7 @@ import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Campfire;
import org.bukkit.block.Furnace;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -789,6 +791,49 @@ public class RecipeEventListener implements Listener {
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)
public void onCraftingRecipe(PrepareItemCraftEvent event) {
if (!Config.enableRecipeSystem()) return;
@@ -843,7 +888,7 @@ public class RecipeEventListener implements Listener {
try {
player = (Player) CraftBukkitReflections.method$InventoryView$getPlayer.invoke(event.getView());
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get inventory viewer", e);
this.plugin.logger().warn("Failed to get inventory viewer", e);
return;
}
@@ -854,14 +899,18 @@ public class RecipeEventListener implements Listener {
if (ceRecipe != null) {
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
serverPlayer.setLastUsedRecipe(ceRecipe.id());
correctCraftingRecipeUsed(inventory, ceRecipe);
if (!ceRecipe.id().equals(recipeId)) {
correctCraftingRecipeUsed(inventory, ceRecipe);
}
return;
}
ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe);
if (ceRecipe != null) {
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
serverPlayer.setLastUsedRecipe(ceRecipe.id());
correctCraftingRecipeUsed(inventory, ceRecipe);
if (!ceRecipe.id().equals(recipeId)) {
correctCraftingRecipeUsed(inventory, ceRecipe);
}
return;
}
// clear result if not met
@@ -869,9 +918,8 @@ public class RecipeEventListener implements Listener {
}
private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe);
Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return;
}
try {
@@ -922,20 +970,21 @@ public class RecipeEventListener implements Listener {
CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe;
ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base));
event.setResult(processed);
correctSmithingRecipeUsed(inventory, ceRecipe);
if (!ceRecipe.id().equals(recipeId)) {
correctSmithingRecipeUsed(inventory, ceRecipe);
}
}
private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe);
Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return;
}
try {
Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory);
CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe);
} 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 java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
@@ -68,8 +67,6 @@ public class BukkitCraftEngine extends CraftEngine {
private SchedulerTask tickTask;
private boolean successfullyLoaded = false;
private boolean successfullyEnabled = false;
private boolean requiresRestart = false;
private boolean hasMod = false;
private AntiGriefLib antiGrief;
private JavaPlugin javaPlugin;
private final Path dataFolderPath;
@@ -90,16 +87,6 @@ public class BukkitCraftEngine extends CraftEngine {
super.logger = logger;
super.platform = new BukkitPlatform();
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");
try {
super.compatibilityManager = (CompatibilityManager) Objects.requireNonNull(ReflectionUtils.getConstructor(compatibilityClass, 0)).newInstance(this);
@@ -132,7 +119,6 @@ public class BukkitCraftEngine extends CraftEngine {
if (super.blockManager == null) {
injectRegistries();
}
if (this.requiresRestart) return;
try {
WorldStorageInjector.init();
} catch (Exception e) {
@@ -177,17 +163,6 @@ public class BukkitCraftEngine extends CraftEngine {
return;
}
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) {
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() {
if (this.antiGrief == null) {
this.antiGrief = AntiGriefLib.builder(this.javaPlugin)

View File

@@ -1,12 +1,32 @@
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.locale.LocalizedResourceConfigException;
import org.bukkit.Bukkit;
import java.util.Map;
public class BukkitPlatform implements Platform {
@Override
public void dispatchCommand(String 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 DebugTargetBlockCommand(this, plugin),
new DebugIsSectionInjectedCommand(this, plugin),
new DebugMigrateTemplatesCommand(this, plugin),
new DebugEntityId2UUIDCommand(this, plugin),
new TotemAnimationCommand(this, plugin),
new EnableResourceCommand(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");
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");
CraftEngineFurniture.place(location, customFurniture, anchorType, playSound);
});

View File

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

View File

@@ -52,7 +52,7 @@ public class EnableResourceCommand extends BukkitCommandFeature<CommandSender> {
return;
}
}
YamlDocument document = plugin().config().loadYamlData(packMetaPath.toFile());
YamlDocument document = plugin().config().loadYamlData(packMetaPath);
document.set("enable", true);
try {
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.core.block.*;
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.ObjectHolder;
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$shape;
private static Field field$CraftEngineBlock$isNoteBlock;
private static Field field$CraftEngineBlock$isTripwire;
public static void init() throws ReflectiveOperationException {
ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17);
@@ -48,6 +50,7 @@ public final class BlockGenerator {
.defineField("behaviorHolder", ObjectHolder.class, Visibility.PUBLIC)
.defineField("shapeHolder", ObjectHolder.class, Visibility.PUBLIC)
.defineField("isClientSideNoteBlock", boolean.class, Visibility.PUBLIC)
.defineField("isClientSideTripwire", boolean.class, Visibility.PUBLIC)
// should always implement this interface
.implement(CoreReflections.clazz$Fallable)
.implement(CoreReflections.clazz$BonemealableBlock)
@@ -55,13 +58,15 @@ public final class BlockGenerator {
// internal interfaces
.implement(BehaviorHolder.class)
.implement(ShapeHolder.class)
.implement(NoteBlockIndicator.class)
.implement(ChainUpdateBlockIndicator.class)
.method(ElementMatchers.named("getBehaviorHolder"))
.intercept(FieldAccessor.ofField("behaviorHolder"))
.method(ElementMatchers.named("getShapeHolder"))
.intercept(FieldAccessor.ofField("shapeHolder"))
.method(ElementMatchers.named("isNoteBlock"))
.intercept(FieldAccessor.ofField("isClientSideNoteBlock"))
.method(ElementMatchers.named("isTripwire"))
.intercept(FieldAccessor.ofField("isClientSideTripwire"))
// getShape
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getShape))
.intercept(MethodDelegation.to(GetShapeInterceptor.INSTANCE))
@@ -142,6 +147,7 @@ public final class BlockGenerator {
field$CraftEngineBlock$behavior = clazz$CraftEngineBlock.getField("behaviorHolder");
field$CraftEngineBlock$shape = clazz$CraftEngineBlock.getField("shapeHolder");
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 {
@@ -155,18 +161,28 @@ public final class BlockGenerator {
field$CraftEngineBlock$behavior.set(newBlockInstance, behaviorHolder);
field$CraftEngineBlock$shape.set(newBlockInstance, shapeHolder);
field$CraftEngineBlock$isNoteBlock.set(newBlockInstance, replacedBlock.equals(BlockKeys.NOTE_BLOCK));
field$CraftEngineBlock$isTripwire.set(newBlockInstance, replacedBlock.equals(BlockKeys.TRIPWIRE));
return newBlockInstance;
}
public static class UpdateShapeInterceptor {
public static final UpdateShapeInterceptor INSTANCE = new UpdateShapeInterceptor();
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
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();
if (((NoteBlockIndicator) thisObj).isNoteBlock() && CoreReflections.clazz$ServerLevel.isInstance(args[levelIndex])) {
startNoteBlockChain(args);
ChainUpdateBlockIndicator indicator = (ChainUpdateBlockIndicator) thisObj;
if (indicator.isNoteBlock()) {
if (CoreReflections.clazz$ServerLevel.isInstance(args[levelIndex])) {
startNoteBlockChain(args);
}
} else if (indicator.isTripwire()) {
if (CoreReflections.clazz$ServerLevel.isInstance(args[posIndex])) {
}
}
try {
return holder.value().updateShape(thisObj, args, superMethod);
@@ -175,30 +191,20 @@ public final class BlockGenerator {
return args[0];
}
}
}
private static void startNoteBlockChain(Object[] args) throws ReflectiveOperationException {
Object direction;
Object serverLevel;
Object blockPos;
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
if (id == 0 || id == 1) {
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
if (id == 1) {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$DOWN, blockPos, 0);
} else {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, 0);
private static void startNoteBlockChain(Object[] args) {
Object direction = args[directionIndex];
Object serverLevel = args[levelIndex];
Object blockPos = args[posIndex];
// Y axis
if (direction == CoreReflections.instance$Direction$DOWN) {
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, CoreReflections.instance$Direction$UP, blockPos, Config.maxNoteBlockChainUpdate());
} else if (direction == CoreReflections.instance$Direction$UP) {
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
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);
Key customRecipeType();
void customRecipeType(Key customRecipeType);
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.plugin.reflection.minecraft.CoreReflections;
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.recipe.CustomCookingRecipe;
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 org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Optional;
public class RecipeInjector {
@@ -41,17 +42,23 @@ public class RecipeInjector {
.name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker")
.implement(CoreReflections.clazz$RecipeManager$CachedCheck)
.implement(InjectedCacheCheck.class)
.defineField("recipeType", Object.class, Visibility.PUBLIC)
.method(ElementMatchers.named("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)
.method(ElementMatchers.named("lastRecipe"))
.intercept(FieldAccessor.ofField("lastRecipe"))
.method(ElementMatchers.named("setLastRecipe"))
.intercept(FieldAccessor.ofField("lastRecipe"))
.defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC)
.method(ElementMatchers.named("lastCustomRecipe"))
.intercept(FieldAccessor.ofField("lastCustomRecipe"))
.method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a")))
.intercept(MethodDelegation.to(
VersionHelper.isOrAbove1_21_2() ?
@@ -73,269 +80,227 @@ public class RecipeInjector {
if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected
Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity);
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);
} else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) {
Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity);
if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected
InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker);
injectedChecker.customRecipeType(RecipeTypes.CAMPFIRE_COOKING);
injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING);
CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker);
}
}
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_20 {
public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20();
@SuppressWarnings("unchecked")
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType();
Object lastRecipe = injectedCacheCheck.lastRecipe();
Optional<Pair<Object, Object>> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe);
if (optionalRecipe.isPresent()) {
Pair<Object, Object> pair = optionalRecipe.get();
Object resourceLocation = pair.getFirst();
Key recipeId = Key.of(resourceLocation.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
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
injectedCacheCheck.lastRecipe(resourceLocation);
return Optional.of(pair.getSecond());
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
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) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(resourceLocation);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond()));
} else {
Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Optional<Pair<Object, Object>> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
if (optionalRecipe.isEmpty()) {
return Optional.empty();
}
Pair<Object, Object> resourceLocationAndRecipe = optionalRecipe.get();
Object rawRecipeResourceLocation = resourceLocationAndRecipe.getFirst();
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) {
injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
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);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
if (ceRecipe == null) {
return Optional.empty();
}
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
}
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
}
}
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_20_5 {
public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5();
@SuppressWarnings("unchecked")
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType();
Object lastRecipe = injectedCacheCheck.lastRecipe();
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe);
if (optionalRecipe.isPresent()) {
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
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
injectedCacheCheck.lastRecipe(id);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
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) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(id);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
if (optionalRecipe.isEmpty()) {
return Optional.empty();
}
Object rawRecipeHolder = optionalRecipe.get();
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) {
injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
if (ceRecipe == null) {
return Optional.empty();
}
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
}
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
}
}
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_21 {
public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21();
@SuppressWarnings("unchecked")
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType();
Object lastRecipe = injectedCacheCheck.lastRecipe();
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe);
if (optionalRecipe.isPresent()) {
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
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
injectedCacheCheck.lastRecipe(id);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
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) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(id);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
if (optionalRecipe.isEmpty()) {
return Optional.empty();
}
Object rawRecipeHolder = optionalRecipe.get();
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) {
injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return optionalRecipe;
}
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0]));
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
if (ceRecipe == null) {
return Optional.empty();
}
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
}
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
}
}
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_21_2 {
public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2();
@SuppressWarnings("unchecked")
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType();
Object lastRecipe = injectedCacheCheck.lastRecipe();
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe);
if (optionalRecipe.isPresent()) {
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);
if (!isCustom) {
injectedCacheCheck.lastRecipe(id);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
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 {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(id);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
Object lastRecipeResourceKey = injectedCacheCheck.lastRecipe();
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey);
if (optionalRecipe.isEmpty()) {
return Optional.empty();
}
// 获取配方的基础信息
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) {
injectedCacheCheck.lastRecipe(rawRecipeResourceKey);
return optionalRecipe;
}
// 获取唯一内存地址id
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0]));
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
// 这个ce配方并不存在那么应该返回空
if (ceRecipe == null) {
return Optional.empty();
}
// 记录上一次使用的配方(ce)
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// 更新上一次使用的配方(nms)
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(ceRecipe.id())));
}
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.util.BlockStateUtils;
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.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -220,23 +221,36 @@ public class WorldStorageInjector {
CESection section = holder.ceSection();
// 如果是原版方块
if (BlockStateUtils.isVanillaBlock(stateId)) {
// 那么应该情况自定义块
// 那么应该清空自定义块
ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE);
// 如果先前不是空气则标记
// 处理 自定义块 -> 原版块
if (!previous.isEmpty()) {
holder.ceChunk().setDirty(true);
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 {
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState);
if (previousImmutableBlockState == immutableBlockState) return;
// 处理 自定义块到自定义块或原版块到自定义块
holder.ceChunk().setDirty(true);
// 不可能!绝对不可能!
if (immutableBlockState.isEmpty()) return;
// 如果新方块的光照属性和客户端认为的不同
if (Config.enableLightSystem() && !immutableBlockState.isEmpty()) {
updateLightIfChanged(holder, previousState, immutableBlockState.vanillaBlockState().handle(), newState, x, y, z);
if (Config.enableLightSystem()) {
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) {
@@ -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();
Object blockPos = LocationUtils.toBlockPos(x, y, z);
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();
List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
world.sectionLightUpdated(pos);
return;
}
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(oldServerSideState, serverSideState, serverWorld, blockPos)) {
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newServerState, oldServerState, 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);

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 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_DECODER = "craftengine_decoder";
@@ -147,9 +147,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.SET_CREATIVE_SLOT, NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket);
registerNMSPacketConsumer(PacketConsumers.LOGIN, NetworkReflections.clazz$ClientboundLoginPacket);
registerNMSPacketConsumer(PacketConsumers.RESPAWN, NetworkReflections.clazz$ClientboundRespawnPacket);
registerNMSPacketConsumer(PacketConsumers.INTERACT_ENTITY, NetworkReflections.clazz$ServerboundInteractPacket);
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.RENAME_ITEM, NetworkReflections.clazz$ServerboundRenameItemPacket);
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.ENTITY_EVENT, NetworkReflections.clazz$ClientboundEntityEventPacket);
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.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
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(PacketConsumers.SET_SCORE_1_20_3, VersionHelper.isOrAbove1_20_3() ? this.packetIds.clientboundSetScorePacket() : -1);
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.SET_ENTITY_DATA, this.packetIds.clientboundSetEntityDataPacket());
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());
registerC2SByteBufPacketConsumer(PacketConsumers.SET_CREATIVE_MODE_SLOT, this.packetIds.serverboundSetCreativeModeSlotPacket());
registerC2SByteBufPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_20, this.packetIds.serverboundContainerClickPacket());
registerC2SByteBufPacketConsumer(PacketConsumers.INTERACT_ENTITY, this.packetIds.serverboundInteractPacket());
}
public static BukkitNetworkManager instance() {

View File

@@ -70,7 +70,6 @@ import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiConsumer;
@@ -85,7 +84,7 @@ public class PacketConsumers {
public static void initEntities(int registrySize) {
ADD_ENTITY_HANDLERS = new BukkitNetworkManager.Handlers[registrySize];
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();
int id = buf.readVarInt();
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.instance$EntityType$TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ITEM_DISPLAY$registryId] = simpleAddEntityHandler(ItemDisplayPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$EGG$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$POTION$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.BLOCK_DISPLAY$registryId] = simpleAddEntityHandler(BlockDisplayPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.EGG$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.POTION$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler();
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();
int id = buf.readVarInt();
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id);
@@ -153,10 +151,12 @@ public class PacketConsumers {
if (Config.hideBaseEntity() && !furniture.hasExternalModel()) {
event.setCancelled(true);
}
} else {
user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE);
}
};
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$INTERACTION$registryId] = (user, event) -> {
if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.instance$EntityType$INTERACTION) return;
ADD_ENTITY_HANDLERS[MEntityTypes.INTERACTION$registryId] = (user, event) -> {
if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.INTERACTION) return;
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
// Cancel collider entity packet
@@ -166,8 +166,8 @@ public class PacketConsumers {
user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE);
}
};
ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$OAK_BOAT$registryId] = (user, event) -> {
if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.instance$EntityType$OAK_BOAT) return;
ADD_ENTITY_HANDLERS[MEntityTypes.OAK_BOAT$registryId] = (user, event) -> {
if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.OAK_BOAT) return;
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
// Cancel collider entity packet
@@ -1106,13 +1106,13 @@ public class PacketConsumers {
Object blockPos = FastNMS.INSTANCE.field$ServerboundPlayerActionPacket$pos(packet);
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
if (VersionHelper.isFolia()) {
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
platformPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> {
try {
handlePlayerActionPacketOnMainThread(player, world, pos, packet);
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPlayerActionPacket", e);
}
}, world, pos.x() >> 4, pos.z() >> 4);
}, () -> {});
} else {
handlePlayerActionPacketOnMainThread(player, world, pos, packet);
}
@@ -1172,21 +1172,21 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HELLO_C2S = (user, event, packet) -> {
try {
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);
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);
} else {
@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()) {
player.setUUID(uuid.get());
} else {
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);
}
};
@@ -1223,10 +1223,10 @@ public class PacketConsumers {
player.clearView();
Object dimensionKey;
if (!VersionHelper.isOrAbove1_20_2()) {
dimensionKey = NetworkReflections.field$ClientboundRespawnPacket$dimension.get(packet);
dimensionKey = NetworkReflections.methodHandle$ClientboundRespawnPacket$dimensionGetter.invokeExact(packet);
} else {
Object commonInfo = NetworkReflections.field$ClientboundRespawnPacket$commonPlayerSpawnInfo.get(packet);
dimensionKey = NetworkReflections.field$CommonPlayerSpawnInfo$dimension.get(commonInfo);
Object commonInfo = NetworkReflections.methodHandle$ClientboundRespawnPacket$commonPlayerSpawnInfoGetter.invokeExact(packet);
dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo);
}
Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey);
World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString())));
@@ -1237,7 +1237,7 @@ public class PacketConsumers {
} else {
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);
}
};
@@ -1251,10 +1251,10 @@ public class PacketConsumers {
if (BukkitNetworkManager.hasViaVersion()) {
user.setProtocolVersion(CraftEngine.instance().compatibilityManager().getPlayerProtocolVersion(player.uuid()));
}
dimensionKey = NetworkReflections.field$ClientboundLoginPacket$dimension.get(packet);
dimensionKey = NetworkReflections.methodHandle$ClientboundLoginPacket$dimensionGetter.invokeExact(packet);
} else {
Object commonInfo = NetworkReflections.field$ClientboundLoginPacket$commonPlayerSpawnInfo.get(packet);
dimensionKey = NetworkReflections.field$CommonPlayerSpawnInfo$dimension.get(commonInfo);
Object commonInfo = NetworkReflections.methodHandle$ClientboundLoginPacket$commonPlayerSpawnInfoGetter.invokeExact(packet);
dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo);
}
Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey);
World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString())));
@@ -1265,7 +1265,7 @@ public class PacketConsumers {
} else {
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);
}
};
@@ -1282,25 +1282,25 @@ public class PacketConsumers {
player.platformPlayer().getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> {
try {
handleSetCreativeSlotPacketOnMainThread(player, packet);
} catch (Exception e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e);
}
}, () -> {});
} else {
handleSetCreativeSlotPacketOnMainThread(player, packet);
}
} catch (Exception e) {
} catch (Throwable 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();
if (bukkitPlayer == null) 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;
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 (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) {
return;
@@ -1373,14 +1373,14 @@ public class PacketConsumers {
if (!user.isOnline()) return;
Player player = (Player) user.platformPlayer();
if (player == null) return;
Object pos = NetworkReflections.field$ServerboundPickItemFromBlockPacket$pos.get(packet);
Object pos = NetworkReflections.methodHandle$ServerboundPickItemFromBlockPacket$posGetter.invokeExact(packet);
if (VersionHelper.isFolia()) {
int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
try {
handlePickItemFromBlockPacketOnMainThread(player, pos);
} catch (Exception e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e);
}
}, player.getWorld(), x >> 4, z >> 4);
@@ -1388,17 +1388,17 @@ public class PacketConsumers {
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
try {
handlePickItemFromBlockPacketOnMainThread(player, pos);
} catch (Exception e) {
} catch (Throwable 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);
}
};
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 blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos);
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
@@ -1411,7 +1411,7 @@ public class PacketConsumers {
// 1.21.4+
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> PICK_ITEM_FROM_ENTITY = (user, event, packet) -> {
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);
if (furniture == null) return;
Player player = (Player) user.platformPlayer();
@@ -1420,7 +1420,7 @@ public class PacketConsumers {
player.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> {
try {
handlePickItemFromEntityOnMainThread(player, furniture);
} catch (Exception e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e);
}
}, () -> {});
@@ -1428,23 +1428,23 @@ public class PacketConsumers {
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
try {
handlePickItemFromEntityOnMainThread(player, furniture);
} catch (Exception e) {
} catch (Throwable 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);
}
};
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();
if (itemId == null) return;
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));
if (itemStack == null) {
CraftEngine.instance().logger().warn("Item: " + itemId + " is not a valid item");
@@ -1453,15 +1453,15 @@ public class PacketConsumers {
assert CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem != null;
if (VersionHelper.isOrAbove1_21_5()) {
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);
} else {
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 {
FriendlyByteBuf buf = event.getBuffer();
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 {
Player player = (Player) user.platformPlayer();
if (player == null) return;
int entityId;
if (BukkitNetworkManager.hasModelEngine()) {
int fakeId = FastNMS.INSTANCE.field$ServerboundInteractPacket$entityId(packet);
entityId = CraftEngine.instance().compatibilityManager().interactionToBaseEntity(fakeId);
} else {
entityId = FastNMS.INSTANCE.field$ServerboundInteractPacket$entityId(packet);
}
FriendlyByteBuf buf = event.getBuffer();
int entityId = BukkitNetworkManager.hasModelEngine() ?
CraftEngine.instance().compatibilityManager().interactionToBaseEntity(buf.readVarInt()) :
buf.readVarInt();
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
if (furniture == null) return;
Object action = NetworkReflections.field$ServerboundInteractPacket$action.get(packet);
Object actionType = NetworkReflections.method$ServerboundInteractPacket$Action$getType.invoke(action);
if (actionType == null) return;
Location location = furniture.baseEntity().getLocation();
int actionType = buf.readVarInt();
BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user;
if (serverPlayer.isSpectatorMode()) return;
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
if (actionType == NetworkReflections.instance$ServerboundInteractPacket$ActionType$ATTACK) {
Player platformPlayer = serverPlayer.platformPlayer();
Location location = furniture.baseEntity().getLocation();
Runnable mainThreadTask;
if (actionType == 1) {
// ATTACK
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.writeBoolean(usingSecondaryAction);
}
mainThreadTask = () -> {
// todo 冒险模式破坏工具白名单
if (serverPlayer.isAdventureMode()) return;
if (furniture.isValid()) {
if (!BukkitCraftEngine.instance().antiGrief().canBreak(player, location)) {
return;
}
FurnitureBreakEvent breakEvent = new FurnitureBreakEvent(serverPlayer.platformPlayer(), furniture);
if (EventUtils.fireAndCheckCancel(breakEvent)) {
return;
}
if (serverPlayer.isAdventureMode() ||
!furniture.isValid() ||
!BukkitCraftEngine.instance().antiGrief().canBreak(platformPlayer, location)
) return;
// execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.POSITION, furniture.position())
);
furniture.config().execute(context, EventTrigger.LEFT_CLICK);
furniture.config().execute(context, EventTrigger.BREAK);
FurnitureBreakEvent breakEvent = new FurnitureBreakEvent(serverPlayer.platformPlayer(), furniture);
if (EventUtils.fireAndCheckCancel(breakEvent))
return;
CraftEngineFurniture.remove(furniture, serverPlayer, !serverPlayer.isCreativeMode(), true);
Cancellable cancellable = Cancellable.of(breakEvent::isCancelled, breakEvent::setCancelled);
// execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.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())
);
furniture.config().execute(context, EventTrigger.LEFT_CLICK);
furniture.config().execute(context, EventTrigger.BREAK);
if (cancellable.isCancelled()) {
return;
}
} 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);
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);
}
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);
}
mainThreadTask = () -> {
FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint);
if (EventUtils.fireAndCheckCancel(interactEvent)) {
return;
}
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
Cancellable cancellable = Cancellable.of(interactEvent::isCancelled, interactEvent::setCancelled);
// execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.HAND, hand)
.withParameter(DirectContextParameters.POSITION, furniture.position())
);
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
AABB hitBox = furniture.aabbByEntityId(entityId);
if (hitBox == null) return;
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
if (itemInHand == null) return;
Optional<CustomItem<ItemStack>> optionalCustomitem = itemInHand.getCustomItem();
Location eyeLocation = player.getEyeLocation();
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
Location eyeLocation = platformPlayer.getEyeLocation();
Vector direction = eyeLocation.getDirection();
Location endLocation = eyeLocation.clone();
endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange()));
@@ -1594,8 +1619,8 @@ public class PacketConsumers {
return;
}
EntityHitResult hitResult = result.get();
if (optionalCustomitem.isPresent() && !optionalCustomitem.get().behaviors().isEmpty()) {
for (ItemBehavior behavior : optionalCustomitem.get().behaviors()) {
if (optionalCustomItem.isPresent() && !optionalCustomItem.get().behaviors().isEmpty()) {
for (ItemBehavior behavior : optionalCustomItem.get().behaviors()) {
if (behavior instanceof FurnitureItemBehavior) {
behavior.useOnBlock(new UseOnContext(serverPlayer, InteractionHand.MAIN_HAND, new BlockHitResult(hitResult.hitLocation(), hitResult.direction(), BlockPos.fromVec3d(hitResult.hitLocation()), false)));
return;
@@ -1604,7 +1629,12 @@ public class PacketConsumers {
}
// now simulate vanilla item behavior
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 {
furniture.findFirstAvailableSeat(entityId).ifPresent(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);
} catch (Exception e) {
return;
} 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);
}
};
@@ -1699,20 +1750,20 @@ public class PacketConsumers {
if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) {
return;
}
String message = (String) NetworkReflections.field$ServerboundRenameItemPacket$name.get(packet);
String message = (String) NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameGetter.invokeExact(packet);
if (message != null && !message.isEmpty()) {
// check bypass
FontManager manager = CraftEngine.instance().fontManager();
IllegalCharacterProcessResult result = manager.processIllegalCharacters(message);
if (result.has()) {
try {
NetworkReflections.field$ServerboundRenameItemPacket$name.set(packet, result.text());
NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameSetter.invokeExact(packet, result.text());
} catch (ReflectiveOperationException 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);
}
};
@@ -1725,7 +1776,7 @@ public class PacketConsumers {
if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) {
return;
}
String[] lines = (String[]) NetworkReflections.field$ServerboundSignUpdatePacket$lines.get(packet);
String[] lines = (String[]) NetworkReflections.methodHandle$ServerboundSignUpdatePacket$linesGetter.invokeExact(packet);
FontManager manager = CraftEngine.instance().fontManager();
if (!manager.isDefaultFontInUse()) return;
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);
}
};
@@ -1756,9 +1807,9 @@ public class PacketConsumers {
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());
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;
if (title.isPresent()) {
@@ -1782,13 +1833,13 @@ public class PacketConsumers {
if (changed) {
Object newPacket = NetworkReflections.constructor$ServerboundEditBookPacket.newInstance(
NetworkReflections.field$ServerboundEditBookPacket$slot.get(packet),
(int) NetworkReflections.methodHandle$ServerboundEditBookPacket$slotGetter.invokeExact(packet),
newPages,
newTitle
);
event.replacePacket(newPacket);
}
} catch (Exception e) {
} catch (Throwable 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) -> {
try {
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)) {
Payload discardedPayload = DiscardedPayload.from(payload);
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);
}
};
@@ -2234,9 +2285,9 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HANDSHAKE_C2S = (user, event, packet) -> {
try {
if (BukkitNetworkManager.hasViaVersion()) return;
int protocolVersion = NetworkReflections.field$ClientIntentionPacket$protocolVersion.getInt(packet);
int protocolVersion = (int) NetworkReflections.methodHandle$ClientIntentionPacket$protocolVersionGetter.invokeExact(packet);
user.setProtocolVersion(protocolVersion);
} catch (Exception e) {
} catch (Throwable 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) -> {
try {
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 == NetworkReflections.instance$ServerboundResourcePackPacket$Action$DECLINED
|| action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD) {
@@ -2267,7 +2318,7 @@ public class PacketConsumers {
if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$SUCCESSFULLY_LOADED) {
user.setSentResourcePack(true);
}
} catch (Exception e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundResourcePackPacket", e);
}
};
@@ -2276,28 +2327,17 @@ public class PacketConsumers {
try {
Object player = user.serverPlayer();
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;
byte eventId = NetworkReflections.field$ClientboundEntityEventPacket$eventId.getByte(packet);
byte eventId = (byte) NetworkReflections.methodHandle$ClientboundEntityEventPacket$eventIdGetter.invokeExact(packet);
if (eventId >= 24 && eventId <= 28) {
CraftEngine.instance().fontManager().refreshEmojiSuggestions(user.uuid());
}
} catch (Exception e) {
} catch (Throwable 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) -> {
try {
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 clientboundBlockEventPacket();
int serverboundContainerClickPacket();
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.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
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.EntityPacketHandler;
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);
if (entityDataId == EntityDataUtils.ITEM_DATA_ID) {
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);
Optional<ItemStack> optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user);
if (optional.isPresent()) {

View File

@@ -11,4 +11,9 @@ public class FurnitureCollisionPacketHandler implements EntityPacketHandler {
public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) {
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) {
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(this.entityId);
buf.writeUUID(uuid);
buf.writeVarInt(MEntityTypes.instance$EntityType$ITEM_DISPLAY$registryId);
buf.writeVarInt(MEntityTypes.ITEM_DISPLAY$registryId);
buf.writeDouble(x);
buf.writeDouble(y);
buf.writeDouble(z);

View File

@@ -139,4 +139,14 @@ public class PacketIds1_20 implements PacketIds {
public int 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");
}
@Override
public int clientboundBlockEventPacket() {
return PacketIdFinder.clientboundByName("minecraft:block_event");
}
@Override
public int serverboundContainerClickPacket() {
return PacketIdFinder.serverboundByName("minecraft:container_click");
@@ -138,4 +143,9 @@ public class PacketIds1_20_5 implements PacketIds {
public int serverboundSetCreativeModeSlotPacket() {
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 io.netty.buffer.ByteBuf;
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.util.BukkitReflectionUtils;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import java.io.BufferedReader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.lang.reflect.*;
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)
);
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(
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(
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 SHORT_GRASS;
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 {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -33,6 +35,8 @@ public final class MBlocks {
ICE = getById("ice");
SHORT_GRASS = getById(VersionHelper.isOrAbove1_20_3() ? "short_grass" : "grass");
SHORT_GRASS$defaultState = CoreReflections.method$Block$defaultBlockState.invoke(SHORT_GRASS);
SHULKER_BOX = getById("shulker_box");
COMPOSTER = getById("composter");
} catch (ReflectiveOperationException 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 {
private MEntityTypes() {}
public static final Object instance$EntityType$TEXT_DISPLAY;
public static final int instance$EntityType$TEXT_DISPLAY$registryId;
public static final Object instance$EntityType$ITEM_DISPLAY;
public static final int instance$EntityType$ITEM_DISPLAY$registryId;
public static final Object instance$EntityType$BLOCK_DISPLAY;
public static final int instance$EntityType$BLOCK_DISPLAY$registryId;
public static final Object instance$EntityType$ARMOR_STAND;
public static final int instance$EntityType$ARMOR_STAND$registryId;
public static final Object instance$EntityType$FALLING_BLOCK;
public static final int instance$EntityType$FALLING_BLOCK$registryId;
public static final Object instance$EntityType$INTERACTION;
public static final int instance$EntityType$INTERACTION$registryId;
public static final Object instance$EntityType$SHULKER;
public static final int instance$EntityType$SHULKER$registryId;
public static final Object instance$EntityType$OAK_BOAT;
public static final int instance$EntityType$OAK_BOAT$registryId;
public static final Object instance$EntityType$TRIDENT;
public static final int instance$EntityType$TRIDENT$registryId;
public static final Object instance$EntityType$SNOWBALL;
public static final int instance$EntityType$SNOWBALL$registryId;
public static final Object instance$EntityType$FIREBALL;
public static final int instance$EntityType$FIREBALL$registryId;
public static final Object instance$EntityType$EYE_OF_ENDER;
public static final int instance$EntityType$EYE_OF_ENDER$registryId;
public static final Object instance$EntityType$FIREWORK_ROCKET;
public static final int instance$EntityType$FIREWORK_ROCKET$registryId;
public static final Object instance$EntityType$ITEM;
public static final int instance$EntityType$ITEM$registryId;
public static final Object instance$EntityType$ITEM_FRAME;
public static final int instance$EntityType$ITEM_FRAME$registryId;
public static final Object instance$EntityType$GLOW_ITEM_FRAME;
public static final int instance$EntityType$GLOW_ITEM_FRAME$registryId;
public static final Object instance$EntityType$OMINOUS_ITEM_SPAWNER;
public static final int instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId;
public static final Object instance$EntityType$SMALL_FIREBALL;
public static final int instance$EntityType$SMALL_FIREBALL$registryId;
public static final Object instance$EntityType$EGG;
public static final int instance$EntityType$EGG$registryId;
public static final Object instance$EntityType$ENDER_PEARL;
public static final int instance$EntityType$ENDER_PEARL$registryId;
public static final Object instance$EntityType$EXPERIENCE_BOTTLE;
public static final int instance$EntityType$EXPERIENCE_BOTTLE$registryId;
public static final Object instance$EntityType$POTION;
public static final int instance$EntityType$POTION$registryId;
public static final Object TEXT_DISPLAY;
public static final int TEXT_DISPLAY$registryId;
public static final Object ITEM_DISPLAY;
public static final int ITEM_DISPLAY$registryId;
public static final Object BLOCK_DISPLAY;
public static final int BLOCK_DISPLAY$registryId;
public static final Object ARMOR_STAND;
public static final int ARMOR_STAND$registryId;
public static final Object FALLING_BLOCK;
public static final int FALLING_BLOCK$registryId;
public static final Object INTERACTION;
public static final int INTERACTION$registryId;
public static final Object SHULKER;
public static final int SHULKER$registryId;
public static final Object OAK_BOAT;
public static final int OAK_BOAT$registryId;
public static final Object TRIDENT;
public static final int TRIDENT$registryId;
public static final Object SNOWBALL;
public static final int SNOWBALL$registryId;
public static final Object FIREBALL;
public static final int FIREBALL$registryId;
public static final Object EYE_OF_ENDER;
public static final int EYE_OF_ENDER$registryId;
public static final Object FIREWORK_ROCKET;
public static final int FIREWORK_ROCKET$registryId;
public static final Object ITEM;
public static final int ITEM$registryId;
public static final Object ITEM_FRAME;
public static final int ITEM_FRAME$registryId;
public static final Object GLOW_ITEM_FRAME;
public static final int GLOW_ITEM_FRAME$registryId;
public static final Object OMINOUS_ITEM_SPAWNER;
public static final int OMINOUS_ITEM_SPAWNER$registryId;
public static final Object SMALL_FIREBALL;
public static final int SMALL_FIREBALL$registryId;
public static final Object EGG;
public static final int EGG$registryId;
public static final Object ENDER_PEARL;
public static final int ENDER_PEARL$registryId;
public static final Object EXPERIENCE_BOTTLE;
public static final int EXPERIENCE_BOTTLE$registryId;
public static final Object POTION;
public static final int POTION$registryId;
private static Object getById(String id) throws ReflectiveOperationException {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -64,50 +64,50 @@ public final class MEntityTypes {
static {
try {
instance$EntityType$TEXT_DISPLAY = getById("text_display");
instance$EntityType$TEXT_DISPLAY$registryId = getRegistryId(instance$EntityType$TEXT_DISPLAY);
instance$EntityType$ITEM_DISPLAY = getById("item_display");
instance$EntityType$ITEM_DISPLAY$registryId = getRegistryId(instance$EntityType$ITEM_DISPLAY);
instance$EntityType$BLOCK_DISPLAY = getById("block_display");
instance$EntityType$BLOCK_DISPLAY$registryId = getRegistryId(instance$EntityType$BLOCK_DISPLAY);
instance$EntityType$FALLING_BLOCK = getById("falling_block");
instance$EntityType$FALLING_BLOCK$registryId = getRegistryId(instance$EntityType$FALLING_BLOCK);
instance$EntityType$INTERACTION = getById("interaction");
instance$EntityType$INTERACTION$registryId = getRegistryId(instance$EntityType$INTERACTION);
instance$EntityType$SHULKER = getById("shulker");
instance$EntityType$SHULKER$registryId = getRegistryId(instance$EntityType$SHULKER);
instance$EntityType$ARMOR_STAND = getById("armor_stand");
instance$EntityType$ARMOR_STAND$registryId = getRegistryId(instance$EntityType$ARMOR_STAND);
instance$EntityType$OAK_BOAT = getById(VersionHelper.isOrAbove1_21_2() ? "oak_boat" : "boat");
instance$EntityType$OAK_BOAT$registryId = getRegistryId(instance$EntityType$OAK_BOAT);
instance$EntityType$TRIDENT = getById("trident");
instance$EntityType$TRIDENT$registryId = getRegistryId(instance$EntityType$TRIDENT);
instance$EntityType$SNOWBALL = getById("snowball");
instance$EntityType$SNOWBALL$registryId = getRegistryId(instance$EntityType$SNOWBALL);
instance$EntityType$FIREBALL = getById("fireball");
instance$EntityType$FIREBALL$registryId = getRegistryId(instance$EntityType$FIREBALL);
instance$EntityType$EYE_OF_ENDER = getById("eye_of_ender");
instance$EntityType$EYE_OF_ENDER$registryId = getRegistryId(instance$EntityType$EYE_OF_ENDER);
instance$EntityType$FIREWORK_ROCKET = getById("firework_rocket");
instance$EntityType$FIREWORK_ROCKET$registryId = getRegistryId(instance$EntityType$FIREWORK_ROCKET);
instance$EntityType$ITEM = getById("item");
instance$EntityType$ITEM$registryId = getRegistryId(instance$EntityType$ITEM);
instance$EntityType$ITEM_FRAME = getById("item_frame");
instance$EntityType$ITEM_FRAME$registryId = getRegistryId(instance$EntityType$ITEM_FRAME);
instance$EntityType$GLOW_ITEM_FRAME = getById("glow_item_frame");
instance$EntityType$GLOW_ITEM_FRAME$registryId = getRegistryId(instance$EntityType$GLOW_ITEM_FRAME);
instance$EntityType$SMALL_FIREBALL = getById("small_fireball");
instance$EntityType$SMALL_FIREBALL$registryId = getRegistryId(instance$EntityType$SMALL_FIREBALL);
instance$EntityType$EGG = getById("egg");
instance$EntityType$EGG$registryId = getRegistryId(instance$EntityType$EGG);
instance$EntityType$ENDER_PEARL = getById("ender_pearl");
instance$EntityType$ENDER_PEARL$registryId = getRegistryId(instance$EntityType$ENDER_PEARL);
instance$EntityType$EXPERIENCE_BOTTLE = getById("experience_bottle");
instance$EntityType$EXPERIENCE_BOTTLE$registryId = getRegistryId(instance$EntityType$EXPERIENCE_BOTTLE);
instance$EntityType$POTION = getById("potion");
instance$EntityType$POTION$registryId = getRegistryId(instance$EntityType$POTION);
instance$EntityType$OMINOUS_ITEM_SPAWNER = VersionHelper.isOrAbove1_20_5() ? getById("ominous_item_spawner") : null;
instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId = getRegistryId(instance$EntityType$OMINOUS_ITEM_SPAWNER);
TEXT_DISPLAY = getById("text_display");
TEXT_DISPLAY$registryId = getRegistryId(TEXT_DISPLAY);
ITEM_DISPLAY = getById("item_display");
ITEM_DISPLAY$registryId = getRegistryId(ITEM_DISPLAY);
BLOCK_DISPLAY = getById("block_display");
BLOCK_DISPLAY$registryId = getRegistryId(BLOCK_DISPLAY);
FALLING_BLOCK = getById("falling_block");
FALLING_BLOCK$registryId = getRegistryId(FALLING_BLOCK);
INTERACTION = getById("interaction");
INTERACTION$registryId = getRegistryId(INTERACTION);
SHULKER = getById("shulker");
SHULKER$registryId = getRegistryId(SHULKER);
ARMOR_STAND = getById("armor_stand");
ARMOR_STAND$registryId = getRegistryId(ARMOR_STAND);
OAK_BOAT = getById(VersionHelper.isOrAbove1_21_2() ? "oak_boat" : "boat");
OAK_BOAT$registryId = getRegistryId(OAK_BOAT);
TRIDENT = getById("trident");
TRIDENT$registryId = getRegistryId(TRIDENT);
SNOWBALL = getById("snowball");
SNOWBALL$registryId = getRegistryId(SNOWBALL);
FIREBALL = getById("fireball");
FIREBALL$registryId = getRegistryId(FIREBALL);
EYE_OF_ENDER = getById("eye_of_ender");
EYE_OF_ENDER$registryId = getRegistryId(EYE_OF_ENDER);
FIREWORK_ROCKET = getById("firework_rocket");
FIREWORK_ROCKET$registryId = getRegistryId(FIREWORK_ROCKET);
ITEM = getById("item");
ITEM$registryId = getRegistryId(ITEM);
ITEM_FRAME = getById("item_frame");
ITEM_FRAME$registryId = getRegistryId(ITEM_FRAME);
GLOW_ITEM_FRAME = getById("glow_item_frame");
GLOW_ITEM_FRAME$registryId = getRegistryId(GLOW_ITEM_FRAME);
SMALL_FIREBALL = getById("small_fireball");
SMALL_FIREBALL$registryId = getRegistryId(SMALL_FIREBALL);
EGG = getById("egg");
EGG$registryId = getRegistryId(EGG);
ENDER_PEARL = getById("ender_pearl");
ENDER_PEARL$registryId = getRegistryId(ENDER_PEARL);
EXPERIENCE_BOTTLE = getById("experience_bottle");
EXPERIENCE_BOTTLE$registryId = getRegistryId(EXPERIENCE_BOTTLE);
POTION = getById("potion");
POTION$registryId = getRegistryId(POTION);
OMINOUS_ITEM_SPAWNER = VersionHelper.isOrAbove1_20_5() ? getById("ominous_item_spawner") : null;
OMINOUS_ITEM_SPAWNER$registryId = getRegistryId(OMINOUS_ITEM_SPAWNER);
} catch (ReflectiveOperationException 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;
public final class MRegistries {
public static final Object instance$Registries$BLOCK;
public static final Object instance$Registries$ITEM;
public static final Object instance$Registries$ATTRIBUTE;
public static final Object instance$Registries$BIOME;
public static final Object instance$Registries$MOB_EFFECT;
public static final Object instance$Registries$SOUND_EVENT;
public static final Object instance$Registries$PARTICLE_TYPE;
public static final Object instance$Registries$ENTITY_TYPE;
public static final Object instance$Registries$FLUID;
public static final Object instance$Registries$RECIPE_TYPE;
public static final Object instance$Registries$DIMENSION_TYPE;
public static final Object instance$Registries$CONFIGURED_FEATURE;
public static final Object instance$Registries$PLACED_FEATURE;
public static final Object BLOCK;
public static final Object ITEM;
public static final Object ATTRIBUTE;
public static final Object BIOME;
public static final Object MOB_EFFECT;
public static final Object SOUND_EVENT;
public static final Object PARTICLE_TYPE;
public static final Object ENTITY_TYPE;
public static final Object FLUID;
public static final Object RECIPE_TYPE;
public static final Object DIMENSION_TYPE;
public static final Object CONFIGURED_FEATURE;
public static final Object PLACED_FEATURE;
@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 {
Field[] fields = CoreReflections.clazz$Registries.getDeclaredFields();
@@ -43,6 +45,7 @@ public final class MRegistries {
Object registries$ConfiguredFeature = null;
Object registries$PlacedFeature = null;
Object registries$JukeboxSong = null;
Object registries$Recipe = null;
for (Field field : fields) {
Type fieldType = field.getGenericType();
if (fieldType instanceof ParameterizedType paramType) {
@@ -60,6 +63,8 @@ public final class MRegistries {
registries$RecipeType = field.get(null);
} else if (rawType == CoreReflections.clazz$ConfiguredFeature) {
registries$ConfiguredFeature = field.get(null);
} else if (rawType == CoreReflections.clazz$Recipe) {
registries$Recipe = field.get(null);
}
} else {
if (type == CoreReflections.clazz$Block) {
@@ -88,20 +93,21 @@ public final class MRegistries {
}
}
}
instance$Registries$BLOCK = requireNonNull(registries$Block);
instance$Registries$ITEM = requireNonNull(registries$Item);
instance$Registries$ATTRIBUTE = requireNonNull(registries$Attribute);
instance$Registries$BIOME = requireNonNull(registries$Biome);
instance$Registries$MOB_EFFECT = requireNonNull(registries$MobEffect);
instance$Registries$SOUND_EVENT = requireNonNull(registries$SoundEvent);
instance$Registries$DIMENSION_TYPE = requireNonNull(registries$DimensionType);
instance$Registries$PARTICLE_TYPE = requireNonNull(registries$ParticleType);
instance$Registries$ENTITY_TYPE = requireNonNull(registries$EntityType);
instance$Registries$FLUID = requireNonNull(registries$Fluid);
instance$Registries$RECIPE_TYPE = requireNonNull(registries$RecipeType);
instance$Registries$CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature);
instance$Registries$PLACED_FEATURE = requireNonNull(registries$PlacedFeature);
instance$Registries$JUKEBOX_SONG = registries$JukeboxSong;
BLOCK = requireNonNull(registries$Block);
ITEM = requireNonNull(registries$Item);
ATTRIBUTE = requireNonNull(registries$Attribute);
BIOME = requireNonNull(registries$Biome);
MOB_EFFECT = requireNonNull(registries$MobEffect);
SOUND_EVENT = requireNonNull(registries$SoundEvent);
DIMENSION_TYPE = requireNonNull(registries$DimensionType);
PARTICLE_TYPE = requireNonNull(registries$ParticleType);
ENTITY_TYPE = requireNonNull(registries$EntityType);
FLUID = requireNonNull(registries$Fluid);
RECIPE_TYPE = requireNonNull(registries$RecipeType);
CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature);
PLACED_FEATURE = requireNonNull(registries$PlacedFeature);
JUKEBOX_SONG = registries$JukeboxSong;
RECIPE = registries$Recipe;
} catch (ReflectiveOperationException 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.VersionHelper;
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.NBTOps;
@@ -34,8 +35,12 @@ public class MRegistryOps {
static {
try {
if (clazz$JavaOps != null) {
// 1.20.5+
Object javaOps = ReflectionUtils.getDeclaredField(clazz$JavaOps, clazz$JavaOps, 0).get(null);
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 {
JAVA = null;
}

View File

@@ -1,10 +1,13 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
import io.netty.buffer.ByteBuf;
import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException;
import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils;
import net.momirealms.craftengine.core.util.ReflectionUtils;
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.Field;
import java.lang.reflect.Method;
@@ -968,7 +971,7 @@ public final class NetworkReflections {
);
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(
@@ -1293,4 +1296,177 @@ public final class NetworkReflections {
public static final Constructor<?> constructor$ClientboundTickingStatePacket = Optional.ofNullable(clazz$ClientboundTickingStatePacket)
.map(it -> ReflectionUtils.getConstructor(it, float.class, boolean.class))
.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.EntityPacketHandler;
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.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
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.WorldEvents;
import org.bukkit.*;
@@ -138,17 +140,17 @@ public class BukkitServerPlayer extends Player {
@Override
public Channel nettyChannel() {
return channel;
return this.channel;
}
@Override
public CraftEngine plugin() {
return plugin;
return this.plugin;
}
@Override
public boolean isMiningBlock() {
return destroyPos != null;
return this.destroyPos != null;
}
public void setDestroyedState(Object destroyedState) {
@@ -221,8 +223,8 @@ public class BukkitServerPlayer extends Player {
@Override
public boolean updateLastSuccessfulInteractionTick(int tick) {
if (lastSuccessfulInteraction != tick) {
lastSuccessfulInteraction = tick;
if (this.lastSuccessfulInteraction != tick) {
this.lastSuccessfulInteraction = tick;
return true;
} else {
return false;
@@ -231,7 +233,7 @@ public class BukkitServerPlayer extends Player {
@Override
public int lastSuccessfulInteractionTick() {
return lastSuccessfulInteraction;
return this.lastSuccessfulInteraction;
}
@Override
@@ -283,8 +285,13 @@ public class BukkitServerPlayer extends Player {
}
@Override
public void playSound(Key sound, float volume, float pitch) {
platformPlayer().playSound(platformPlayer(), sound.toString(), SoundCategory.MASTER, volume, pitch);
public void playSound(Key sound, SoundSource source, float volume, float 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
@@ -535,8 +542,10 @@ public class BukkitServerPlayer extends Player {
public void abortMiningBlock() {
this.swingHandAck = false;
this.miningProgress = 0;
if (this.destroyPos != null) {
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
BlockPos pos = this.destroyPos;
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();
// optimize break speed, otherwise it would be too fast
if (currentTick - this.lastSuccessfulBreak <= 5) return;
Object destroyedState = this.destroyedState;
if (destroyedState == null) return;
try {
org.bukkit.entity.Player player = platformPlayer();
double range = getCachedInteractionRange();
@@ -575,7 +586,7 @@ public class BukkitServerPlayer extends Player {
// send hit sound if the sound is removed
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 soundEvent = CoreReflections.field$SoundType$hitSound.get(soundType);
Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent);
@@ -601,8 +612,8 @@ public class BukkitServerPlayer extends Player {
}
}
float progressToAdd = getDestroyProgress(this.destroyedState, hitPos);
int id = BlockStateUtils.blockStateToId(this.destroyedState);
float progressToAdd = getDestroyProgress(destroyedState, hitPos);
int id = BlockStateUtils.blockStateToId(destroyedState);
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(id);
// double check custom block
if (customState != null && !customState.isEmpty()) {
@@ -612,13 +623,13 @@ public class BukkitServerPlayer extends Player {
// it's correct on plugin side
if (blockSettings.isCorrectTool(item.id())) {
// 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
progressToAdd = progressToAdd * (10f / 3f);
}
} else {
// 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();
}
}
@@ -681,7 +692,7 @@ public class BukkitServerPlayer extends Player {
double d1 = (double) hitPos.y() - otherLocation.getY();
double d2 = (double) hitPos.z() - otherLocation.getZ();
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) {
this.miningProgress = 0;
this.isDestroyingBlock = is;
this.isDestroyingCustomBlock = custom && is;
if (is) {
this.swingHandAck = true;
this.isDestroyingCustomBlock = custom;
} else {
this.swingHandAck = false;
this.destroyedState = null;
if (this.destroyPos != null) {
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
// 只纠正自定义方块的
if (this.isDestroyingCustomBlock) {
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
}
this.destroyPos = null;
}
this.isDestroyingCustomBlock = false;
}
}

View File

@@ -26,7 +26,7 @@ public class BukkitSoundManager extends AbstractSoundManager {
protected void registerSongs(Map<Key, JukeboxSong> songs) {
if (songs.isEmpty()) return;
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);
for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) {
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.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.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.NetworkReflections;
import net.momirealms.craftengine.core.block.*;
@@ -22,18 +24,26 @@ import org.jetbrains.annotations.Nullable;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class BlockStateUtils {
public static final IdentityHashMap<Object, Object> CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>();
private static int vanillaStateSize;
private static boolean hasInit;
public static Map<Object, Integer> IGNITE_ODDS;
@SuppressWarnings("unchecked")
public static void init(int size) {
if (hasInit) {
throw new IllegalStateException("BlockStateUtils has already been initialized");
}
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;
}
@@ -248,4 +258,9 @@ public class BlockStateUtils {
public static int 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);
if (value == null) {
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);
return value;
} 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.VersionHelper;
import org.bukkit.Bukkit;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
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) {
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) {
throw new RuntimeException(e);
}

View File

@@ -92,6 +92,7 @@ public class InteractUtils {
keyReference, item.getItem()
))) != null).isPresent();
});
registerInteraction(BlockKeys.DECORATED_POT, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.HOPPER, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.DISPENSER, (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);
if (value == null) {
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);
return value;
} catch (Exception e) {

View File

@@ -1,19 +1,19 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.plugin.config.Config;
public class NoteBlockChainUpdateUtils {
private NoteBlockChainUpdateUtils() {}
// TODO 都在一个区块内应该优化到区块内的方块getter
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 state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos);
if (BlockStateUtils.isClientSideNoteBlock(state)) {
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() {
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);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);

View File

@@ -1,20 +1,14 @@
package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
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.bukkit.util.*;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockInWorld;
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.*;
import net.momirealms.craftengine.core.world.particle.ParticleData;
import org.bukkit.Location;
import org.bukkit.Particle;
@@ -124,4 +118,9 @@ public class BukkitWorld implements World {
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z);
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
- /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:
enable: true
permission: ce.command.debug.test

View File

@@ -49,12 +49,14 @@ resource-pack:
bypass-textures:
# - minecraft:block/farmland
- "@legacy_unicode"
- "@vanilla_font_textures"
- "@vanilla_item_textures"
- "@vanilla_block_textures"
bypass-models: []
- "@vanilla_textures"
bypass-models:
- "@vanilla_models"
bypass-sounds: []
bypass-equipments: []
# Validate if there are any errors in the resource pack, such as missing textures or models
validate:
enable: true
supported-version:
min: "1.20"
max: LATEST
@@ -336,7 +338,7 @@ gui:
performance:
# 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
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:acacia_sapling[stage=1]: minecraft:acacia_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
# Sculk Sensor

View File

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

View File

@@ -2,7 +2,7 @@ templates:
default:emoji/basic:
content: "<hover:show_text:'<i18n:emoji.tip>'><!shadow><white><arg:emoji></white></!shadow></hover>"
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:
default:emoji_location:

View File

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

View File

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

View File

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

View File

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