mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-27 02:49:15 +00:00
Merge branch 'dev' into dev-changue-block-timer-behavior
This commit is contained in:
@@ -75,7 +75,7 @@ repositories {
|
||||
```
|
||||
```kotlin
|
||||
dependencies {
|
||||
compileOnly("net.momirealms:craft-engine-core:0.0.59")
|
||||
compileOnly("net.momirealms:craft-engine-bukkit:0.0.59")
|
||||
compileOnly("net.momirealms:craft-engine-core:0.0.61")
|
||||
compileOnly("net.momirealms:craft-engine-bukkit:0.0.61")
|
||||
}
|
||||
```
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-beta13"
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.CustomFishingProvider;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsProvider;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.MythicMobsProvider;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.CustomFishingSource;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsSource;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.MythicMobsSource;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsSource;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.leveler.*;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelModel;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineModel;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineUtils;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicMobsListener;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDropListener;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicSkillHelper;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
|
||||
@@ -39,6 +40,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
private final Map<String, LevelerProvider> levelerProviders;
|
||||
private boolean hasPlaceholderAPI;
|
||||
private boolean hasViaVersion;
|
||||
private MythicSkillHelper skillExecute;
|
||||
|
||||
public BukkitCompatibilityManager(BukkitCraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
@@ -130,12 +132,17 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
logHook("EcoJobs");
|
||||
}
|
||||
if (this.isPluginEnabled("MythicMobs")) {
|
||||
BukkitItemManager.instance().registerExternalItemProvider(new MythicMobsProvider());
|
||||
new MythicMobsListener(this.plugin);
|
||||
BukkitItemManager.instance().registerExternalItemSource(new MythicMobsSource());
|
||||
new MythicItemDropListener(this.plugin);
|
||||
logHook("MythicMobs");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeMMSkill(String skill, float power, Player player) {
|
||||
MythicSkillHelper.execute(skill, power, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerLevelerProvider(String plugin, LevelerProvider provider) {
|
||||
this.levelerProviders.put(plugin, provider);
|
||||
@@ -247,15 +254,15 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
private void initItemHooks() {
|
||||
BukkitItemManager itemManager = BukkitItemManager.instance();
|
||||
if (this.isPluginEnabled("NeigeItems")) {
|
||||
itemManager.registerExternalItemProvider(new NeigeItemsProvider());
|
||||
itemManager.registerExternalItemSource(new NeigeItemsSource());
|
||||
logHook("NeigeItems");
|
||||
}
|
||||
if (this.isPluginEnabled("MMOItems")) {
|
||||
itemManager.registerExternalItemProvider(new MMOItemsProvider());
|
||||
itemManager.registerExternalItemSource(new MMOItemsSource());
|
||||
logHook("MMOItems");
|
||||
}
|
||||
if (this.isPluginEnabled("CustomFishing")) {
|
||||
itemManager.registerExternalItemProvider(new CustomFishingProvider());
|
||||
itemManager.registerExternalItemSource(new CustomFishingSource());
|
||||
logHook("CustomFishing");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.item;
|
||||
|
||||
import net.momirealms.craftengine.core.item.ExternalItemProvider;
|
||||
import net.momirealms.craftengine.core.item.ExternalItemSource;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
|
||||
import net.momirealms.customfishing.api.mechanic.context.Context;
|
||||
@@ -11,10 +11,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class CustomFishingProvider implements ExternalItemProvider<ItemStack> {
|
||||
public class CustomFishingSource implements ExternalItemSource<ItemStack> {
|
||||
@Override
|
||||
public String plugin() {
|
||||
return "CustomFishing";
|
||||
return "customfishing";
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.compatibility.item;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.Type;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.momirealms.craftengine.core.item.ExternalItemProvider;
|
||||
import net.momirealms.craftengine.core.item.ExternalItemSource;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -11,22 +11,26 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class MMOItemsProvider implements ExternalItemProvider<ItemStack> {
|
||||
public class MMOItemsSource implements ExternalItemSource<ItemStack> {
|
||||
|
||||
@Override
|
||||
public String plugin() {
|
||||
return "MMOItems";
|
||||
return "mmoitems";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack build(String id, ItemBuildContext context) {
|
||||
String[] split = id.split(":", 2);
|
||||
if (split.length == 1) {
|
||||
split = split[0].split("_", 2);
|
||||
}
|
||||
if (split.length == 1) return new ItemStack(Material.AIR);
|
||||
MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase());
|
||||
return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id(ItemStack item) {
|
||||
return MMOItems.getType(item) + ":" + MMOItems.getID(item);
|
||||
return MMOItems.getType(item) + "_" + MMOItems.getID(item);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
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.ExternalItemSource;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MythicMobsProvider implements ExternalItemProvider<ItemStack> {
|
||||
public class MythicMobsSource implements ExternalItemSource<ItemStack> {
|
||||
private MythicBukkit mythicBukkit;
|
||||
|
||||
@Override
|
||||
public String plugin() {
|
||||
return "MythicMobs";
|
||||
return "mythicmobs";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1,6 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.item;
|
||||
|
||||
import net.momirealms.craftengine.core.item.ExternalItemProvider;
|
||||
import net.momirealms.craftengine.core.item.ExternalItemSource;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -8,11 +8,11 @@ import pers.neige.neigeitems.manager.ItemManager;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class NeigeItemsProvider implements ExternalItemProvider<ItemStack> {
|
||||
public class NeigeItemsSource implements ExternalItemSource<ItemStack> {
|
||||
|
||||
@Override
|
||||
public String plugin() {
|
||||
return "NeigeItems";
|
||||
return "neigeitems";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -21,12 +21,12 @@ import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
public class CraftEngineItemDrop extends ItemDrop implements IItemDrop {
|
||||
public class MythicItemDrop 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) {
|
||||
public MythicItemDrop(String line, MythicLineConfig config, CustomItem<ItemStack> customItem) {
|
||||
super(line, config);
|
||||
this.customItem = customItem;
|
||||
}
|
||||
@@ -8,10 +8,10 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
public class MythicMobsListener implements Listener {
|
||||
public class MythicItemDropListener implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public MythicMobsListener(BukkitCraftEngine plugin) {
|
||||
public MythicItemDropListener(BukkitCraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.javaPlugin());
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public class MythicMobsListener implements Listener {
|
||||
this.plugin.itemManager().getCustomItem(itemId).ifPresent(customItem -> {
|
||||
String line = event.getContainer().getConfigLine();
|
||||
MythicLineConfig config = event.getConfig();
|
||||
event.register(new CraftEngineItemDrop(line, config, customItem));
|
||||
event.register(new MythicItemDrop(line, config, customItem));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.mythicmobs;
|
||||
|
||||
import io.lumine.mythic.bukkit.MythicBukkit;
|
||||
import io.lumine.mythic.core.utils.MythicUtil;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class MythicSkillHelper {
|
||||
|
||||
public static void execute(String skill, float power, Player player) {
|
||||
org.bukkit.entity.Player casterPlayer = (org.bukkit.entity.Player) player.platformPlayer();
|
||||
Location location = casterPlayer.getLocation();
|
||||
LivingEntity target = MythicUtil.getTargetedEntity(casterPlayer);
|
||||
List<Entity> targets = new ArrayList<>();
|
||||
List<Location> locations = null;
|
||||
if (target != null) {
|
||||
targets.add(target);
|
||||
locations = List.of(target.getLocation());
|
||||
}
|
||||
MythicBukkit.inst().getAPIHelper().castSkill(casterPlayer, skill, casterPlayer, location, targets, locations, power);
|
||||
}
|
||||
}
|
||||
@@ -46,14 +46,12 @@ import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
|
||||
private static int[] ordinalToIbdID;
|
||||
private final Extent extent;
|
||||
private final Set<CEChunk> chunksToSave;
|
||||
private final CEWorld ceWorld;
|
||||
private final Set<ChunkPos> brokenChunks = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
protected FastAsyncWorldEditDelegate(EditSessionEvent event, Extent extent) {
|
||||
super(extent);
|
||||
this.extent = extent;
|
||||
this.chunksToSave = new HashSet<>();
|
||||
World weWorld = event.getWorld();
|
||||
org.bukkit.World world = Bukkit.getWorld(requireNonNull(weWorld).getName());
|
||||
@@ -77,8 +75,9 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
|
||||
public void onEditSessionEvent(EditSessionEvent event) {
|
||||
World weWorld = event.getWorld();
|
||||
if (weWorld == null) return;
|
||||
Extent currentExtent = event.getExtent();
|
||||
if (event.getStage() == EditSession.Stage.BEFORE_CHANGE) {
|
||||
event.setExtent(new FastAsyncWorldEditDelegate(event, event.getExtent()));
|
||||
event.setExtent(new FastAsyncWorldEditDelegate(event, currentExtent));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -90,12 +89,14 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
|
||||
if (levelChunk != null) {
|
||||
Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk);
|
||||
CESection[] ceSections = ceChunk.sections();
|
||||
for (int i = 0; i < ceSections.length; i++) {
|
||||
CESection ceSection = ceSections[i];
|
||||
Object section = sections[i];
|
||||
int finalI = i;
|
||||
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
|
||||
(injected) -> sections[finalI] = injected);
|
||||
synchronized (sections) {
|
||||
for (int i = 0; i < ceSections.length; i++) {
|
||||
CESection ceSection = ceSections[i];
|
||||
Object section = sections[i];
|
||||
int finalI = i;
|
||||
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
|
||||
(injected) -> sections[finalI] = injected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +139,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
|
||||
|
||||
@Override
|
||||
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) {
|
||||
boolean result = extent.setBlock(x, y, z, block);
|
||||
boolean result = super.setBlock(x, y, z, block);
|
||||
if (result) {
|
||||
Mask mask = getMask();
|
||||
if (mask != null && !mask.test(BlockVector3.at(x, y, z))) return true;
|
||||
@@ -175,18 +176,18 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
|
||||
|
||||
@Override
|
||||
public @Nullable Operation commit() {
|
||||
Operation operation = super.commit();
|
||||
saveAllChunks();
|
||||
Operation operation = super.commit();
|
||||
List<ChunkPos> chunks = new ArrayList<>(this.brokenChunks);
|
||||
this.brokenChunks.clear();
|
||||
Object worldServer = this.ceWorld.world().serverWorld();
|
||||
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
|
||||
for (ChunkPos chunk : chunks) {
|
||||
CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey());
|
||||
// only inject loaded chunks
|
||||
if (loaded == null) continue;
|
||||
injectLevelChunk(chunkSource, loaded);
|
||||
}
|
||||
Object worldServer = this.ceWorld.world().serverWorld();
|
||||
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
|
||||
for (ChunkPos chunk : chunks) {
|
||||
CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey());
|
||||
// only inject loaded chunks
|
||||
if (loaded == null) continue;
|
||||
injectLevelChunk(chunkSource, loaded);
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
|
||||
@@ -215,11 +216,12 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
|
||||
try {
|
||||
CEChunk ceChunk = Optional.ofNullable(this.ceWorld.getChunkAtIfLoaded(chunkX, chunkZ))
|
||||
.orElse(this.ceWorld.worldDataStorage().readChunkAt(this.ceWorld, new ChunkPos(chunkX, chunkZ)));
|
||||
CESection ceSection = ceChunk.sectionById(SectionPos.blockToSectionCoord(blockY));
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(newStateId);
|
||||
if (immutableBlockState == null) {
|
||||
ceChunk.setBlockState(blockX, blockY, blockZ, EmptyBlock.STATE);
|
||||
ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, EmptyBlock.STATE);
|
||||
} else {
|
||||
ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState);
|
||||
ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, immutableBlockState);
|
||||
}
|
||||
this.chunksToSave.add(ceChunk);
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-beta13"
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-beta13"
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("de.eldoria.plugin-yml.bukkit") version "0.7.1"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import net.minecrell.pluginyml.paper.PaperPluginDescription
|
||||
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-beta13"
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("de.eldoria.plugin-yml.paper") version "0.7.1"
|
||||
}
|
||||
|
||||
|
||||
@@ -129,13 +129,12 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
|
||||
// set block side properties
|
||||
CoreReflections.field$BlockBehaviour$explosionResistance.set(nmsBlock, settings.resistance());
|
||||
CoreReflections.field$BlockBehaviour$soundType.set(nmsBlock, SoundUtils.toSoundType(settings.sounds()));
|
||||
// 1.21.2以前要在init cache之前设定 isConditionallyFullOpaque
|
||||
// init cache
|
||||
CoreReflections.method$BlockStateBase$initCache.invoke(nmsState);
|
||||
boolean isConditionallyFullOpaque = canOcclude & useShapeForLightOcclusion;
|
||||
if (!VersionHelper.isOrAbove1_21_2()) {
|
||||
CoreReflections.field$BlockStateBase$isConditionallyFullOpaque.set(nmsState, isConditionallyFullOpaque);
|
||||
}
|
||||
// init cache
|
||||
CoreReflections.method$BlockStateBase$initCache.invoke(nmsState);
|
||||
// modify cache
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().handle());
|
||||
|
||||
@@ -27,6 +27,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
public static final Key STAIRS_BLOCK = Key.from("craftengine:stairs_block");
|
||||
public static final Key PRESSURE_PLATE_BLOCK = Key.from("craftengine:pressure_plate_block");
|
||||
public static final Key DOUBLE_BLOCK = Key.from("craftengine:double_block");
|
||||
public static final Key DOUBLE_HIGH_BLOCK = Key.from("craftengine:double_high_block");
|
||||
public static final Key CHANGE_OVER_TIME_BLOCK = Key.from("craftengine:change_over_time_block");
|
||||
public static void init() {
|
||||
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
|
||||
@@ -52,6 +53,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
register(STAIRS_BLOCK, StairsBlockBehavior.FACTORY);
|
||||
register(PRESSURE_PLATE_BLOCK, PressurePlateBlockBehavior.FACTORY);
|
||||
register(DOUBLE_BLOCK, DoubleBlockBehavior.FACTORY);
|
||||
register(DOUBLE_HIGH_BLOCK, DoubleHighBlockBehavior.FACTORY);
|
||||
register(CHANGE_OVER_TIME_BLOCK,ChangeOverTimeBlockBehavior.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
@@ -20,12 +19,11 @@ import net.momirealms.craftengine.core.world.*;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
||||
public class DoubleBlockBehavior extends BukkitBlockBehavior {
|
||||
public class DoubleHighBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final Property<DoubleBlockHalf> halfProperty;
|
||||
|
||||
public DoubleBlockBehavior(CustomBlock customBlock, Property<DoubleBlockHalf> halfProperty) {
|
||||
public DoubleHighBlockBehavior(CustomBlock customBlock, Property<DoubleBlockHalf> halfProperty) {
|
||||
super(customBlock);
|
||||
this.halfProperty = halfProperty;
|
||||
}
|
||||
@@ -83,8 +81,8 @@ public class DoubleBlockBehavior extends BukkitBlockBehavior {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Property<DoubleBlockHalf> half = (Property<DoubleBlockHalf>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.double.missing_half");
|
||||
return new DoubleBlockBehavior(block, half);
|
||||
Property<DoubleBlockHalf> half = (Property<DoubleBlockHalf>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.double_high.missing_half");
|
||||
return new DoubleHighBlockBehavior(block, half);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureElement;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||
import net.momirealms.craftengine.core.util.Color;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import org.bukkit.Material;
|
||||
@@ -65,7 +66,7 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized List<Object> getCachedValues(@Nullable Integer color, int @Nullable [] colors) {
|
||||
private synchronized List<Object> getCachedValues(@Nullable Color color, int @Nullable [] colors) {
|
||||
List<Object> cachedValues = new ArrayList<>(this.commonValues);
|
||||
Item<ItemStack> item = BukkitItemManager.instance().createWrappedItem(item(), null);
|
||||
if (item == null) {
|
||||
|
||||
@@ -21,12 +21,12 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
private final Object item;
|
||||
private final Object clientItem;
|
||||
|
||||
public BukkitCustomItem(UniqueKey id, Object item, Object clientItem, Key materialKey, Key clientBoundMaterialKey,
|
||||
public BukkitCustomItem(boolean isVanillaItem, UniqueKey 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, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events);
|
||||
super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events);
|
||||
this.item = item;
|
||||
this.clientItem = clientItem;
|
||||
}
|
||||
@@ -42,13 +42,13 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<ItemStack> buildItem(ItemBuildContext context) {
|
||||
ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.constructor$ItemStack(this.item, 1));
|
||||
public Item<ItemStack> buildItem(ItemBuildContext context, int count) {
|
||||
ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.constructor$ItemStack(this.item, count));
|
||||
Item<ItemStack> wrapped = BukkitCraftEngine.instance().itemManager().wrap(item);
|
||||
for (ItemDataModifier<ItemStack> modifier : dataModifiers()) {
|
||||
modifier.apply(wrapped, context);
|
||||
}
|
||||
return BukkitCraftEngine.instance().itemManager().wrap(wrapped.getItem());
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
public Object clientItem() {
|
||||
@@ -64,6 +64,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
}
|
||||
|
||||
public static class BuilderImpl implements Builder<ItemStack> {
|
||||
private boolean isVanillaItem;
|
||||
private UniqueKey id;
|
||||
private Key itemKey;
|
||||
private final Object item;
|
||||
@@ -80,6 +81,12 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
this.clientBoundItem = clientBoundItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> isVanillaItem(boolean is) {
|
||||
this.isVanillaItem = is;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> id(UniqueKey id) {
|
||||
this.id = id;
|
||||
@@ -150,7 +157,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
public CustomItem<ItemStack> build() {
|
||||
this.modifiers.addAll(this.settings.modifiers());
|
||||
this.clientBoundModifiers.addAll(this.settings.clientBoundModifiers());
|
||||
return new BukkitCustomItem(this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors),
|
||||
return new BukkitCustomItem(this.isVanillaItem, 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,14 +12,12 @@ import net.momirealms.craftengine.bukkit.item.listener.DebugStickListener;
|
||||
import net.momirealms.craftengine.bukkit.item.listener.ItemEventListener;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*;
|
||||
import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.*;
|
||||
import net.momirealms.craftengine.core.item.modifier.IdModifier;
|
||||
import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult;
|
||||
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
|
||||
import net.momirealms.craftengine.core.pack.AbstractPackManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
@@ -28,8 +26,6 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce
|
||||
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -39,6 +35,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
static {
|
||||
@@ -56,8 +53,10 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
private final Object bedrockItemHolder;
|
||||
private final Item<ItemStack> emptyItem;
|
||||
private final UniqueIdItem<ItemStack> emptyUniqueItem;
|
||||
private final Function<Object, Integer> decoratedHashOpsGenerator;
|
||||
private Set<Key> lastRegisteredPatterns = Set.of();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public BukkitItemManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
instance = this;
|
||||
@@ -68,13 +67,28 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
this.armorEventListener = new ArmorEventListener();
|
||||
this.networkItemHandler = VersionHelper.isOrAbove1_20_5() ? new ModernNetworkItemHandler() : new LegacyNetworkItemHandler();
|
||||
this.registerAllVanillaItems();
|
||||
this.bedrockItemHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, KeyUtils.toResourceLocation(Key.of("minecraft:bedrock")))).get();;
|
||||
this.bedrockItemHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, KeyUtils.toResourceLocation(Key.of("minecraft:bedrock")))).get();
|
||||
this.registerCustomTrimMaterial();
|
||||
this.loadLastRegisteredPatterns();
|
||||
|
||||
ItemStack emptyStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.instance$ItemStack$EMPTY);
|
||||
this.emptyItem = this.wrap(emptyStack);
|
||||
this.emptyUniqueItem = new UniqueIdItem<>(UniqueKey.AIR, this.emptyItem);
|
||||
this.emptyItem = this.factory.wrap(emptyStack);
|
||||
this.emptyUniqueItem = UniqueIdItem.of(this.emptyItem);
|
||||
this.decoratedHashOpsGenerator = VersionHelper.isOrAbove1_21_5() ? (Function<Object, Integer>) FastNMS.INSTANCE.createDecoratedHashOpsGenerator(MRegistryOps.HASHCODE) : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void delayedLoad() {
|
||||
super.delayedLoad();
|
||||
List<ExternalItemSource<ItemStack>> sources = new ArrayList<>();
|
||||
for (String externalSource : Config.recipeIngredientSources()) {
|
||||
String sourceId = externalSource.toLowerCase(Locale.ENGLISH);
|
||||
ExternalItemSource<ItemStack> provider = getExternalItemSource(sourceId);
|
||||
if (provider != null) {
|
||||
sources.add(provider);
|
||||
}
|
||||
}
|
||||
this.factory.resetRecipeIngredientSources(sources.isEmpty() ? null : sources.toArray(new ExternalItemSource[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,13 +157,33 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<ItemStack> build(DatapackRecipeResult result) {
|
||||
if (result.components() == null) {
|
||||
ItemStack itemStack = createVanillaItemStack(Key.of(result.id()));
|
||||
return wrap(itemStack).count(result.count());
|
||||
} else {
|
||||
// 低版本无法应用nbt或组件,所以这里是1.20.5+
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("id", result.id());
|
||||
jsonObject.addProperty("count", result.count());
|
||||
jsonObject.add("components", result.components());
|
||||
Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject)
|
||||
.resultOrPartial((itemId) -> plugin.logger().severe("Tried to load invalid item: '" + itemId + "'")).orElse(null);
|
||||
if (nmsStack == null) {
|
||||
return this.emptyItem;
|
||||
}
|
||||
return wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BuildableItem<ItemStack>> getVanillaItem(Key key) {
|
||||
Material material = Registry.MATERIAL.get(KeyUtils.toNamespacedKey(key));
|
||||
if (material == null) {
|
||||
ItemStack vanilla = createVanillaItemStack(key);
|
||||
if (vanilla == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(new CloneableConstantItem(key, new ItemStack(material)));
|
||||
return Optional.of(CloneableConstantItem.of(this.wrap(vanilla)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -341,31 +375,18 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
|
||||
@Override
|
||||
public @NotNull Item<ItemStack> wrap(ItemStack itemStack) {
|
||||
if (itemStack == null) return this.emptyItem;
|
||||
if (itemStack == null || itemStack.isEmpty()) return this.emptyItem;
|
||||
return this.factory.wrap(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key itemId(ItemStack itemStack) {
|
||||
Item<ItemStack> wrapped = wrap(itemStack);
|
||||
return wrapped.id();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key customItemId(ItemStack itemStack) {
|
||||
Item<ItemStack> wrapped = wrap(itemStack);
|
||||
if (!wrapped.hasTag(IdModifier.CRAFT_ENGINE_ID)) return null;
|
||||
return wrapped.id();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CustomItem.Builder<ItemStack> createPlatformItemBuilder(UniqueKey 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) {
|
||||
if (item == MItems.AIR) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.invalid_material", materialId.toString());
|
||||
}
|
||||
if (clientBoundItem == null) {
|
||||
if (clientBoundItem == MItems.AIR) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.invalid_material", clientBoundMaterialId.toString());
|
||||
}
|
||||
return BukkitCustomItem.builder(item, clientBoundItem)
|
||||
@@ -437,4 +458,9 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
}
|
||||
return this.emptyItem;
|
||||
}
|
||||
|
||||
@Nullable // 1.21.5+
|
||||
public Function<Object, Integer> decoratedHashOpsGenerator() {
|
||||
return decoratedHashOpsGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package net.momirealms.craftengine.bukkit.item;
|
||||
|
||||
import net.momirealms.craftengine.core.item.BuildableItem;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class CloneableConstantItem implements BuildableItem<ItemStack> {
|
||||
private final ItemStack item;
|
||||
private final Key id;
|
||||
|
||||
public CloneableConstantItem(Key id, ItemStack item) {
|
||||
this.item = item;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItemStack(ItemBuildContext context, int count) {
|
||||
ItemStack itemStack = this.item.clone();
|
||||
itemStack.setAmount(count);
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.NetworkItemHandler;
|
||||
import net.momirealms.craftengine.core.item.modifier.ArgumentModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
@@ -74,7 +74,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
return new OtherItem(wrapped, hasDifferentMaterial).process();
|
||||
} else {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
Tag argumentTag = wrapped.getTag(ArgumentModifier.ARGUMENTS_TAG);
|
||||
Tag argumentTag = wrapped.getTag(ArgumentsModifier.ARGUMENTS_TAG);
|
||||
ItemBuildContext context;
|
||||
if (argumentTag instanceof CompoundTag arguments) {
|
||||
ContextHolder.Builder builder = ContextHolder.builder();
|
||||
|
||||
@@ -4,7 +4,7 @@ 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;
|
||||
import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
@@ -60,6 +60,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
|
||||
@Override
|
||||
public Optional<Item<ItemStack>> s2c(Item<ItemStack> wrapped, Player player) {
|
||||
Item<ItemStack> original = wrapped;
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
|
||||
if (optionalCustomItem.isEmpty()) {
|
||||
if (!Config.interceptItem()) return Optional.empty();
|
||||
@@ -76,7 +77,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
return new OtherItem(wrapped, hasDifferentMaterial).process();
|
||||
} else {
|
||||
CompoundTag customData = Optional.ofNullable(wrapped.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA)).map(CompoundTag.class::cast).orElse(new CompoundTag());
|
||||
CompoundTag arguments = customData.getCompound(ArgumentModifier.ARGUMENTS_TAG);
|
||||
CompoundTag arguments = customData.getCompound(ArgumentsModifier.ARGUMENTS_TAG);
|
||||
ItemBuildContext context;
|
||||
if (arguments == null) {
|
||||
context = ItemBuildContext.of(player);
|
||||
@@ -89,7 +90,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
}
|
||||
CompoundTag tag = new CompoundTag();
|
||||
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) {
|
||||
modifier.prepareNetworkItem(wrapped, context, tag);
|
||||
modifier.prepareNetworkItem(original, context, tag);
|
||||
}
|
||||
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) {
|
||||
modifier.apply(wrapped, context);
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
@@ -54,7 +55,7 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
public InteractionResult useOnBlock(UseOnContext context) {
|
||||
Player player = context.getPlayer();
|
||||
// no adventure mode for the moment
|
||||
if (player.isAdventureMode()) {
|
||||
if (player != null && player.isAdventureMode()) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@@ -66,9 +67,9 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
Optional<StrippableBlockBehavior> behaviorOptional = customState.behavior().getAs(StrippableBlockBehavior.class);
|
||||
if (behaviorOptional.isEmpty()) return InteractionResult.PASS;
|
||||
Key stripped = behaviorOptional.get().stripped();
|
||||
Item<ItemStack> offHandItem = (Item<ItemStack>) player.getItemInHand(InteractionHand.OFF_HAND);
|
||||
Item<ItemStack> offHandItem = player != null ? (Item<ItemStack>) player.getItemInHand(InteractionHand.OFF_HAND) : BukkitItemManager.instance().uniqueEmptyItem().item();
|
||||
// is using a shield
|
||||
if (context.getHand() == InteractionHand.MAIN_HAND && !ItemUtils.isEmpty(offHandItem) && canBlockAttack(offHandItem) && !player.isSecondaryUseActive()) {
|
||||
if (context.getHand() == InteractionHand.MAIN_HAND && !ItemUtils.isEmpty(offHandItem) && canBlockAttack(offHandItem) && player != null && !player.isSecondaryUseActive()) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@@ -81,13 +82,15 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
CompoundTag compoundTag = customState.propertiesNbt();
|
||||
ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag);
|
||||
|
||||
org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
|
||||
|
||||
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
|
||||
// Call bukkit event
|
||||
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
|
||||
if (EventUtils.fireAndCheckCancel(event)) {
|
||||
return InteractionResult.FAIL;
|
||||
org.bukkit.entity.Player bukkitPlayer = null;
|
||||
if (player != null) {
|
||||
bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
|
||||
// Call bukkit event
|
||||
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
|
||||
if (EventUtils.fireAndCheckCancel(event)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
|
||||
@@ -98,28 +101,30 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags());
|
||||
clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z()));
|
||||
Material material = MaterialUtils.getMaterial(item.vanillaId());
|
||||
bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1);
|
||||
if (bukkitPlayer != null) {
|
||||
bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1);
|
||||
|
||||
// resend swing if it's not interactable on client side
|
||||
if (!InteractUtils.isInteractable(
|
||||
bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().handle()),
|
||||
context.getHitResult(), item
|
||||
) || player.isSecondaryUseActive()) {
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
// shrink item amount
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
Object itemStack = item.getLiteralObject();
|
||||
Object serverPlayer = player.serverPlayer();
|
||||
Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND;
|
||||
try {
|
||||
CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to hurt itemStack", e);
|
||||
// resend swing if it's not interactable on client side
|
||||
if (!InteractUtils.isInteractable(
|
||||
bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().handle()),
|
||||
context.getHitResult(), item
|
||||
) || player.isSecondaryUseActive()) {
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
// shrink item amount
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
Object itemStack = item.getLiteralObject();
|
||||
Object serverPlayer = player.serverPlayer();
|
||||
Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND;
|
||||
try {
|
||||
CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to hurt itemStack", e);
|
||||
}
|
||||
} else {
|
||||
ItemStack itemStack = item.getItem();
|
||||
itemStack.damage(1, bukkitPlayer);
|
||||
}
|
||||
} else {
|
||||
ItemStack itemStack = item.getItem();
|
||||
itemStack.damage(1, bukkitPlayer);
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@@ -30,10 +30,7 @@ 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.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.Cancellable;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -50,6 +47,8 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -66,6 +65,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
return this.place(new BlockPlaceContext(context));
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public InteractionResult place(BlockPlaceContext context) {
|
||||
Optional<CustomBlock> optionalBlock = BukkitBlockManager.instance().blockById(this.blockId);
|
||||
if (optionalBlock.isEmpty()) {
|
||||
@@ -76,11 +76,14 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
Player player = context.getPlayer();
|
||||
CustomBlock block = optionalBlock.get();
|
||||
BlockPos pos = context.getClickedPos();
|
||||
int maxY = context.getLevel().worldHeight().getMaxBuildHeight() - 1;
|
||||
if (context.getClickedFace() == Direction.UP && pos.y() >= maxY) {
|
||||
context.getPlayer().sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED));
|
||||
if (player != null) {
|
||||
player.sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED));
|
||||
}
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
@@ -89,55 +92,66 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
Player player = context.getPlayer();
|
||||
BlockPos againstPos = context.getAgainstPos();
|
||||
World world = (World) context.getLevel().platformWorld();
|
||||
Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z());
|
||||
Block bukkitBlock = world.getBlockAt(placeLocation);
|
||||
Block againstBlock = world.getBlockAt(againstPos.x(), againstPos.y(), againstPos.z());
|
||||
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
|
||||
org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null;
|
||||
|
||||
if (player.isAdventureMode()) {
|
||||
Object againstBlockState = BlockStateUtils.blockDataToBlockState(againstBlock.getBlockData());
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(againstBlockState);
|
||||
if (optionalCustomState.isEmpty()) {
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState customState = optionalCustomState.get();
|
||||
// custom block
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().handle() : againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
if (player != null) {
|
||||
|
||||
if (player.isAdventureMode()) {
|
||||
Object againstBlockState = BlockStateUtils.blockDataToBlockState(againstBlock.getBlockData());
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(againstBlockState);
|
||||
if (optionalCustomState.isEmpty()) {
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState customState = optionalCustomState.get();
|
||||
// custom block
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().handle() : againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trigger event
|
||||
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
|
||||
DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand());
|
||||
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
|
||||
return InteractionResult.FAIL;
|
||||
// trigger event
|
||||
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
|
||||
DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand());
|
||||
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// it's just world + pos
|
||||
BlockState previousState = bukkitBlock.getState();
|
||||
List<BlockState> revertStates = new ArrayList<>(2);
|
||||
revertStates.add(previousState);
|
||||
// place custom block
|
||||
CraftEngineBlocks.place(placeLocation, blockStateToPlace, UpdateOption.UPDATE_ALL_IMMEDIATE, false);
|
||||
// call bukkit event
|
||||
BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
|
||||
if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) {
|
||||
// revert changes
|
||||
previousState.update(true, false);
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
placeBlock(placeLocation, blockStateToPlace, revertStates);
|
||||
|
||||
// call custom event
|
||||
CustomBlockPlaceEvent customPlaceEvent = new CustomBlockPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, world.getBlockAt(placeLocation), context.getHand());
|
||||
if (EventUtils.fireAndCheckCancel(customPlaceEvent)) {
|
||||
// revert changes
|
||||
previousState.update(true, false);
|
||||
return InteractionResult.FAIL;
|
||||
if (player != null) {
|
||||
// call bukkit event
|
||||
BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
|
||||
if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) {
|
||||
// revert changes
|
||||
for (BlockState state : revertStates) {
|
||||
state.update(true, false);
|
||||
}
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
// call custom event
|
||||
CustomBlockPlaceEvent customPlaceEvent = new CustomBlockPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, world.getBlockAt(placeLocation), context.getHand());
|
||||
if (EventUtils.fireAndCheckCancel(customPlaceEvent)) {
|
||||
// revert changes
|
||||
for (BlockState state : revertStates) {
|
||||
state.update(true, false);
|
||||
}
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5);
|
||||
@@ -154,14 +168,15 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
return InteractionResult.SUCCESS_AND_CANCEL;
|
||||
}
|
||||
|
||||
if (!player.isCreativeMode()) {
|
||||
Item<?> item = context.getItem();
|
||||
item.count(item.count() - 1);
|
||||
if (player != null) {
|
||||
if (!player.isCreativeMode()) {
|
||||
Item<?> item = context.getItem();
|
||||
item.count(item.count() - 1);
|
||||
}
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
|
||||
block.setPlacedBy(context, blockStateToPlace);
|
||||
|
||||
player.swingHand(context.getHand());
|
||||
context.getLevel().playBlockSound(position, blockStateToPlace.settings().sounds().placeSound());
|
||||
world.sendGameEvent(bukkitPlayer, GameEvent.BLOCK_PLACE, new Vector(pos.x(), pos.y(), pos.z()));
|
||||
return InteractionResult.SUCCESS;
|
||||
@@ -176,18 +191,30 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
protected boolean canPlace(BlockPlaceContext context, ImmutableBlockState state) {
|
||||
try {
|
||||
Object player = context.getPlayer().serverPlayer();
|
||||
Player cePlayer = context.getPlayer();
|
||||
Object player = cePlayer != null ? cePlayer.serverPlayer() : null;
|
||||
Object blockState = state.customBlockState().handle();
|
||||
Object blockPos = LocationUtils.toBlockPos(context.getClickedPos());
|
||||
Object voxelShape = CoreReflections.method$CollisionContext$of.invoke(null, player);
|
||||
Object voxelShape;
|
||||
if (VersionHelper.isOrAbove1_21_6()) {
|
||||
voxelShape = CoreReflections.method$CollisionContext$placementContext.invoke(null, player);
|
||||
} else if (player != null) {
|
||||
voxelShape = CoreReflections.method$CollisionContext$of.invoke(null, player);
|
||||
} else {
|
||||
voxelShape = CoreReflections.instance$CollisionContext$empty;
|
||||
}
|
||||
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel((World) context.getLevel().platformWorld());
|
||||
boolean defaultReturn = ((!this.checkStatePlacement() || (boolean) CoreReflections.method$BlockStateBase$canSurvive.invoke(blockState, world, blockPos))
|
||||
&& (boolean) CoreReflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true));
|
||||
boolean defaultReturn = ((!this.checkStatePlacement() || FastNMS.INSTANCE.method$BlockStateBase$canSurvive(blockState, world, blockPos))
|
||||
&& (boolean) CoreReflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true)); // paper only
|
||||
Block block = FastNMS.INSTANCE.method$CraftBlock$at(world, blockPos);
|
||||
BlockData blockData = FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState);
|
||||
BlockCanBuildEvent canBuildEvent = new BlockCanBuildEvent(block, (org.bukkit.entity.Player) context.getPlayer().platformPlayer(), blockData, defaultReturn, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
|
||||
BlockCanBuildEvent canBuildEvent = new BlockCanBuildEvent(
|
||||
block, cePlayer != null ? (org.bukkit.entity.Player) cePlayer.platformPlayer() : null, blockData, defaultReturn,
|
||||
context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND
|
||||
);
|
||||
Bukkit.getPluginManager().callEvent(canBuildEvent);
|
||||
return canBuildEvent.isBuildable();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -196,6 +223,10 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean placeBlock(Location location, ImmutableBlockState blockState, List<BlockState> revertStates) {
|
||||
return CraftEngineBlocks.place(location, blockState, UpdateOption.UPDATE_ALL_IMMEDIATE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key block() {
|
||||
return this.blockId;
|
||||
|
||||
@@ -11,6 +11,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
|
||||
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 final Key AXE_ITEM = Key.from("craftengine:axe_item");
|
||||
public static final Key DOUBLE_HIGH_BLOCK_ITEM = Key.from("craftengine:double_high_block_item");
|
||||
|
||||
public static void init() {
|
||||
register(EMPTY, EmptyItemBehavior.FACTORY);
|
||||
@@ -20,5 +21,6 @@ public class BukkitItemBehaviors extends ItemBehaviors {
|
||||
register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY);
|
||||
register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY);
|
||||
register(AXE_ITEM, AxeItemBehavior.FACTORY);
|
||||
register(DOUBLE_HIGH_BLOCK_ITEM, DoubleHighBlockItemBehavior.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ 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.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
@@ -44,29 +45,35 @@ public class CompostableItemBehavior extends ItemBehavior {
|
||||
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);
|
||||
|
||||
Player player = context.getPlayer();
|
||||
if (willRaise) {
|
||||
levelled.setLevel(currentLevel + 1);
|
||||
EntityChangeBlockEvent event = new EntityChangeBlockEvent((Entity) context.getPlayer().platformPlayer(), block.block(), levelled);
|
||||
if (EventUtils.fireAndCheckCancel(event)) {
|
||||
return InteractionResult.FAIL;
|
||||
if (player != null) {
|
||||
EntityChangeBlockEvent event = new EntityChangeBlockEvent((Entity) player.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));
|
||||
((World) context.getLevel().platformWorld()).sendGameEvent(player != null ? (Entity) player.platformPlayer() : null, GameEvent.BLOCK_CHANGE, new Vector(block.x() + 0.5, block.y() + 0.5, block.z() + 0.5));
|
||||
if (currentLevel + 1 == 7) {
|
||||
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), blockOwner, 20);
|
||||
}
|
||||
if (!context.getPlayer().canInstabuild()) {
|
||||
context.getItem().shrink(1);
|
||||
if (player != null) {
|
||||
if (!player.canInstabuild()) {
|
||||
context.getItem().shrink(1);
|
||||
}
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
context.getPlayer().swingHand(context.getHand());
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DoubleHighBlockItemBehavior extends BlockItemBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
|
||||
public DoubleHighBlockItemBehavior(Key blockId) {
|
||||
super(blockId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean placeBlock(Location location, ImmutableBlockState blockState, List<BlockState> revertState) {
|
||||
Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld());
|
||||
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY() + 1, location.getBlockZ());
|
||||
UpdateOption option = UpdateOption.builder().updateNeighbors().updateClients().updateImmediate().updateKnownShape().build();
|
||||
Object fluidData = FastNMS.INSTANCE.method$BlockGetter$getFluidState(level, blockPos);
|
||||
Object stateToPlace = fluidData == MFluids.WATER$defaultState ? MBlocks.WATER$defaultState : MBlocks.AIR$defaultState;
|
||||
revertState.add(location.getWorld().getBlockAt(location.getBlockX(), location.getBlockY() + 1, location.getBlockZ()).getState());
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, stateToPlace, option.flags());
|
||||
return super.placeBlock(location, blockState, revertState);
|
||||
}
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
|
||||
Object id = arguments.get("block");
|
||||
if (id == null) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block", new IllegalArgumentException("Missing required parameter 'block' for double_high_block_item behavior"));
|
||||
}
|
||||
if (id instanceof Map<?, ?> map) {
|
||||
if (map.containsKey(key.toString())) {
|
||||
// 防呆
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false));
|
||||
} else {
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
}
|
||||
return new DoubleHighBlockItemBehavior(key);
|
||||
} else {
|
||||
return new DoubleHighBlockItemBehavior(Key.of(id.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,9 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public InteractionResult useOnBlock(UseOnContext context) {
|
||||
net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer();
|
||||
if (player == null) return InteractionResult.PASS;
|
||||
|
||||
BlockPos clickedPos = context.getClickedPos();
|
||||
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(clickedPos);
|
||||
Block block = clicked.block();
|
||||
@@ -65,7 +68,6 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer();
|
||||
// 点击对象直接可燃,则忽略
|
||||
if (isClickedBlockBurnable) {
|
||||
int stateId = BlockStateUtils.blockStateToId(clickedBlockState);
|
||||
|
||||
@@ -77,14 +77,14 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
|
||||
Player player = context.getPlayer();
|
||||
// todo adventure check
|
||||
if (player.isAdventureMode()) {
|
||||
if (player != null && player.isAdventureMode()) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
Vec3d clickedPosition = context.getClickLocation();
|
||||
|
||||
// trigger event
|
||||
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
|
||||
org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null;
|
||||
World world = (World) context.getLevel().platformWorld();
|
||||
|
||||
// get position and rotation for placement
|
||||
@@ -100,7 +100,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z());
|
||||
}
|
||||
} else {
|
||||
furnitureYaw = placement.rotationRule().apply(180 + player.xRot());
|
||||
furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0));
|
||||
Pair<Double, Double> xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z()));
|
||||
finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right());
|
||||
}
|
||||
@@ -121,10 +121,12 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(),
|
||||
DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z()));
|
||||
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
|
||||
return InteractionResult.FAIL;
|
||||
if (player != null) {
|
||||
FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(),
|
||||
DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z()));
|
||||
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
Item<?> item = context.getItem();
|
||||
@@ -140,10 +142,12 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
.fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null))
|
||||
.build(), false);
|
||||
|
||||
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand());
|
||||
if (EventUtils.fireAndCheckCancel(placeEvent)) {
|
||||
bukkitFurniture.destroy();
|
||||
return InteractionResult.FAIL;
|
||||
if (player != null) {
|
||||
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand());
|
||||
if (EventUtils.fireAndCheckCancel(placeEvent)) {
|
||||
bukkitFurniture.destroy();
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
Cancellable dummy = Cancellable.dummy();
|
||||
@@ -159,12 +163,14 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
return InteractionResult.SUCCESS_AND_CANCEL;
|
||||
}
|
||||
|
||||
if (!player.isCreativeMode()) {
|
||||
item.count(item.count() - 1);
|
||||
if (player != null) {
|
||||
if (!player.canInstabuild()) {
|
||||
item.count(item.count() - 1);
|
||||
}
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
|
||||
context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound());
|
||||
player.swingHand(context.getHand());
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.world.BlockHitResult;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
@@ -40,8 +41,9 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult use(World world, Player player, InteractionHand hand) {
|
||||
public InteractionResult use(World world, @Nullable Player player, InteractionHand hand) {
|
||||
try {
|
||||
if (player == null) return InteractionResult.FAIL;
|
||||
Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY);
|
||||
Object blockPos = CoreReflections.field$BlockHitResul$blockPos.get(blockHitResult);
|
||||
BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + offsetY, FastNMS.INSTANCE.field$Vec3i$z(blockPos));
|
||||
|
||||
@@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
|
||||
import net.momirealms.craftengine.bukkit.util.ItemTags;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.core.item.ExternalItemSource;
|
||||
import net.momirealms.craftengine.core.item.ItemFactory;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.item.ItemWrapper;
|
||||
@@ -13,6 +14,7 @@ import net.momirealms.craftengine.core.item.data.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.craftengine.core.util.StringUtils;
|
||||
import net.momirealms.craftengine.core.util.UniqueKey;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -22,6 +24,8 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extends ItemFactory<W, ItemStack> {
|
||||
private boolean hasExternalRecipeSource = false;
|
||||
private ExternalItemSource<ItemStack>[] recipeIngredientSources = null;
|
||||
|
||||
protected BukkitItemFactory(CraftEngine plugin) {
|
||||
super(plugin);
|
||||
@@ -45,13 +49,28 @@ public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extend
|
||||
case "1.21.4" -> {
|
||||
return new ComponentItemFactory1_21_4(plugin);
|
||||
}
|
||||
case "1.21.5", "1.21.6", "1.21.7", "1.22", "1.22.1" -> {
|
||||
case "1.21.5", "1.21.6", "1.21.7", "1.21.8" -> {
|
||||
return new ComponentItemFactory1_21_5(plugin);
|
||||
}
|
||||
default -> throw new IllegalStateException("Unsupported server version: " + plugin.serverVersion());
|
||||
}
|
||||
}
|
||||
|
||||
public void resetRecipeIngredientSources(ExternalItemSource<ItemStack>[] recipeIngredientSources) {
|
||||
if (recipeIngredientSources == null || recipeIngredientSources.length == 0) {
|
||||
this.recipeIngredientSources = null;
|
||||
this.hasExternalRecipeSource = false;
|
||||
} else {
|
||||
this.recipeIngredientSources = recipeIngredientSources;
|
||||
this.hasExternalRecipeSource = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEmpty(W item) {
|
||||
return FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected byte[] toByteArray(W item) {
|
||||
@@ -72,6 +91,9 @@ public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extend
|
||||
|
||||
@Override
|
||||
protected Key id(W item) {
|
||||
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject())) {
|
||||
return ItemKeys.AIR;
|
||||
}
|
||||
return customId(item).orElse(vanillaId(item));
|
||||
}
|
||||
|
||||
@@ -82,18 +104,25 @@ public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extend
|
||||
|
||||
@Override
|
||||
protected UniqueKey recipeIngredientID(W item) {
|
||||
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject())) {
|
||||
return null;
|
||||
}
|
||||
if (this.hasExternalRecipeSource) {
|
||||
for (ExternalItemSource<ItemStack> source : this.recipeIngredientSources) {
|
||||
String id = source.id(item.getItem());
|
||||
if (id != null) {
|
||||
return UniqueKey.create(Key.of(source.plugin(), StringUtils.toLowerCase(id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return UniqueKey.create(id(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean is(W item, Key itemTag) {
|
||||
protected boolean hasItemTag(W item, Key itemTag) {
|
||||
Object literalObject = item.getLiteralObject();
|
||||
Object tag = ItemTags.getOrCreate(itemTag);
|
||||
try {
|
||||
return (boolean) CoreReflections.method$ItemStack$isTag.invoke(literalObject, tag);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
return false;
|
||||
}
|
||||
return FastNMS.INSTANCE.method$ItemStack$is(literalObject, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,20 +10,20 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInReg
|
||||
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.attribute.AttributeModifier;
|
||||
import net.momirealms.craftengine.core.item.ComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.data.Enchantment;
|
||||
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||
import net.momirealms.craftengine.core.item.data.Trim;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import net.momirealms.sparrow.nbt.ListTag;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemWrapper> {
|
||||
|
||||
@@ -31,11 +31,6 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEmpty(ComponentItemWrapper item) {
|
||||
return item.getItem().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customId(ComponentItemWrapper item, Key id) {
|
||||
FastNMS.INSTANCE.setCustomItemId(item.getLiteralObject(), id.toString());
|
||||
@@ -128,7 +123,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
valueTag = MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, value);
|
||||
}
|
||||
|
||||
CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(new CompoundTag());
|
||||
CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElseGet(CompoundTag::new);
|
||||
|
||||
if (path == null || path.length == 0) {
|
||||
if (valueTag instanceof CompoundTag) {
|
||||
@@ -386,23 +381,23 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Integer> dyedColor(ComponentItemWrapper item) {
|
||||
protected Optional<Color> dyedColor(ComponentItemWrapper item) {
|
||||
if (!item.hasComponent(ComponentTypes.DYED_COLOR)) return Optional.empty();
|
||||
Object javaObj = getJavaComponent(item, ComponentTypes.DYED_COLOR);
|
||||
if (javaObj instanceof Integer integer) {
|
||||
return Optional.of(integer);
|
||||
return Optional.of(Color.fromDecimal(integer));
|
||||
} else if (javaObj instanceof Map<?, ?> map) {
|
||||
return Optional.of((int) map.get("rgb"));
|
||||
return Optional.of(Color.fromDecimal((int) map.get("rgb")));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dyedColor(ComponentItemWrapper item, Integer color) {
|
||||
protected void dyedColor(ComponentItemWrapper item, Color color) {
|
||||
if (color == null) {
|
||||
item.resetComponent(ComponentTypes.DYED_COLOR);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.DYED_COLOR, color);
|
||||
item.setJavaComponent(ComponentTypes.DYED_COLOR, color.color());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,4 +581,26 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
Object itemStack2 = FastNMS.INSTANCE.method$ItemStack$transmuteCopy(itemStack1, newItem, amount);
|
||||
return new ComponentItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack2));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attributeModifiers(ComponentItemWrapper item, List<AttributeModifier> modifierList) {
|
||||
CompoundTag compoundTag = (CompoundTag) item.getSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS).orElseGet(CompoundTag::new);
|
||||
ListTag modifiers = new ListTag();
|
||||
compoundTag.put("modifiers", modifiers);
|
||||
for (AttributeModifier modifier : modifierList) {
|
||||
CompoundTag modifierTag = new CompoundTag();
|
||||
modifierTag.putString("type", modifier.type());
|
||||
modifierTag.putString("slot", modifier.slot().name().toLowerCase(Locale.ENGLISH));
|
||||
if (VersionHelper.isOrAbove1_21()) {
|
||||
modifierTag.putString("id", modifier.id().toString());
|
||||
} else {
|
||||
modifierTag.putIntArray("uuid", UUIDUtils.uuidToIntArray(UUID.nameUUIDFromBytes(modifier.id().toString().getBytes(StandardCharsets.UTF_8))));
|
||||
modifierTag.putString("name", modifier.id().toString());
|
||||
}
|
||||
modifierTag.putDouble("amount", modifier.amount());
|
||||
modifierTag.putString("operation", modifier.operation().id());
|
||||
modifiers.add(modifierTag);
|
||||
}
|
||||
item.setSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, compoundTag);
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,20 @@ import com.google.gson.JsonElement;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
|
||||
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
|
||||
import net.momirealms.craftengine.core.attribute.AttributeModifier;
|
||||
import net.momirealms.craftengine.core.item.ComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.data.JukeboxPlayable;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import net.momirealms.craftengine.core.util.GsonHelper;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import net.momirealms.sparrow.nbt.ListTag;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
@@ -123,4 +128,29 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) {
|
||||
item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, data.song());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attributeModifiers(ComponentItemWrapper item, List<AttributeModifier> modifierList) {
|
||||
ListTag modifiers = new ListTag();
|
||||
for (AttributeModifier modifier : modifierList) {
|
||||
CompoundTag modifierTag = new CompoundTag();
|
||||
modifierTag.putString("type", modifier.type());
|
||||
modifierTag.putString("slot", modifier.slot().name().toLowerCase(Locale.ENGLISH));
|
||||
modifierTag.putString("id", modifier.id().toString());
|
||||
modifierTag.putDouble("amount", modifier.amount());
|
||||
modifierTag.putString("operation", modifier.operation().id());
|
||||
AttributeModifier.Display display = modifier.display();
|
||||
if (VersionHelper.isOrAbove1_21_6() && display != null) {
|
||||
CompoundTag displayTag = new CompoundTag();
|
||||
AttributeModifier.Display.Type displayType = display.type();
|
||||
displayTag.putString("type", displayType.name().toLowerCase(Locale.ENGLISH));
|
||||
if (displayType == AttributeModifier.Display.Type.OVERRIDE) {
|
||||
displayTag.put("value", AdventureHelper.componentToTag(display.value()));
|
||||
}
|
||||
modifierTag.put("display", displayTag);
|
||||
}
|
||||
modifiers.add(modifierTag);
|
||||
}
|
||||
item.setSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,18 @@ 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.attribute.AttributeModifier;
|
||||
import net.momirealms.craftengine.core.item.data.Enchantment;
|
||||
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||
import net.momirealms.craftengine.core.item.data.Trim;
|
||||
import net.momirealms.craftengine.core.item.modifier.IdModifier;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Color;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.SkullUtils;
|
||||
import net.momirealms.craftengine.core.util.UUIDUtils;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import net.momirealms.sparrow.nbt.ListTag;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
@@ -62,11 +67,6 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
|
||||
return item.remove(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEmpty(LegacyItemWrapper item) {
|
||||
return item.getItem().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Key> customId(LegacyItemWrapper item) {
|
||||
Object id = item.getJavaTag(IdModifier.CRAFT_ENGINE_ID);
|
||||
@@ -169,17 +169,17 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Integer> dyedColor(LegacyItemWrapper item) {
|
||||
protected Optional<Color> dyedColor(LegacyItemWrapper item) {
|
||||
if (!item.hasTag("display", "color")) return Optional.empty();
|
||||
return Optional.of(item.getJavaTag("display", "color"));
|
||||
return Optional.of(Color.fromDecimal(item.getJavaTag("display", "color")));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dyedColor(LegacyItemWrapper item, Integer color) {
|
||||
protected void dyedColor(LegacyItemWrapper item, Color color) {
|
||||
if (color == null) {
|
||||
item.remove("display", "color");
|
||||
} else {
|
||||
item.setTag(color, "display", "color");
|
||||
item.setTag(color.color(), "display", "color");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,4 +343,20 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
|
||||
FastNMS.INSTANCE.method$ItemStack$setTag(newItemStack, FastNMS.INSTANCE.method$CompoundTag$copy(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item.getLiteralObject())));
|
||||
return new LegacyItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(newItemStack));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attributeModifiers(LegacyItemWrapper item, List<AttributeModifier> modifiers) {
|
||||
ListTag listTag = new ListTag();
|
||||
for (AttributeModifier modifier : modifiers) {
|
||||
CompoundTag modifierTag = new CompoundTag();
|
||||
modifierTag.putString("AttributeName", modifier.type());
|
||||
modifierTag.putString("Name", modifier.id().toString());
|
||||
modifierTag.putString("Slot", modifier.slot().name().toLowerCase(Locale.ENGLISH));
|
||||
modifierTag.putInt("Operation", modifier.operation().ordinal());
|
||||
modifierTag.putDouble("Amount", modifier.amount());
|
||||
modifierTag.putIntArray("UUID", UUIDUtils.uuidToIntArray(UUID.nameUUIDFromBytes(modifier.id().toString().getBytes(StandardCharsets.UTF_8))));
|
||||
listTag.add(modifierTag);
|
||||
}
|
||||
item.setTag(listTag, "AttributeModifiers");
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Openable;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
@@ -66,14 +67,22 @@ public class ItemEventListener implements Listener {
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onInteractEntity(PlayerInteractEntityEvent event) {
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(event.getPlayer());
|
||||
Player player = event.getPlayer();
|
||||
Entity entity = event.getRightClicked();
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
if (serverPlayer == null) return;
|
||||
|
||||
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
|
||||
// prevent duplicated interact air events
|
||||
serverPlayer.updateLastInteractEntityTick(hand);
|
||||
|
||||
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(hand);
|
||||
|
||||
if (ItemUtils.isEmpty(itemInHand)) return;
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
|
||||
if (optionalCustomItem.isEmpty()) return;
|
||||
// 如果目标实体与手中物品可以产生交互,那么忽略
|
||||
if (InteractUtils.isEntityInteractable(player, entity, itemInHand)) return;
|
||||
|
||||
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
|
||||
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
|
||||
@@ -263,25 +272,7 @@ public class ItemEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
// execute item right click functions
|
||||
if (hasCustomItem) {
|
||||
Cancellable dummy = Cancellable.dummy();
|
||||
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
|
||||
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
|
||||
.withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState)
|
||||
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
|
||||
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation()))
|
||||
.withParameter(DirectContextParameters.HAND, hand)
|
||||
.withParameter(DirectContextParameters.EVENT, dummy)
|
||||
);
|
||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||
customItem.execute(context, EventTrigger.RIGHT_CLICK);
|
||||
if (dummy.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 优先检查物品行为,再执行自定义事件
|
||||
// 检查其他的物品行为,物品行为理论只在交互时处理
|
||||
Optional<List<ItemBehavior>> optionalItemBehaviors = itemInHand.getItemBehavior();
|
||||
// 物品类型是否包含自定义物品行为,行为不一定来自于自定义物品,部分原版物品也包含了新的行为
|
||||
@@ -309,8 +300,31 @@ public class ItemEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行物品右键事件
|
||||
if (hasCustomItem) {
|
||||
// 要求服务端侧这个方块不可交互,或玩家处于潜行状态
|
||||
if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) {
|
||||
Cancellable dummy = Cancellable.dummy();
|
||||
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
|
||||
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
|
||||
.withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState)
|
||||
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
|
||||
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation()))
|
||||
.withParameter(DirectContextParameters.HAND, hand)
|
||||
.withParameter(DirectContextParameters.EVENT, dummy)
|
||||
);
|
||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||
customItem.execute(context, EventTrigger.RIGHT_CLICK);
|
||||
if (dummy.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行物品左键事件
|
||||
if (hasCustomItem && action == Action.LEFT_CLICK_BLOCK) {
|
||||
Cancellable dummy = Cancellable.dummy();
|
||||
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
|
||||
@@ -340,11 +354,16 @@ public class ItemEventListener implements Listener {
|
||||
return;
|
||||
// Gets the item in hand
|
||||
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
|
||||
// prevents duplicated events
|
||||
if (serverPlayer.lastInteractEntityCheck(hand)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(hand);
|
||||
// should never be null
|
||||
if (ItemUtils.isEmpty(itemInHand)) return;
|
||||
|
||||
// todo 真的需要这个吗
|
||||
// TODO 有必要存在吗?
|
||||
if (cancelEventIfHasInteraction(event, serverPlayer, hand)) {
|
||||
return;
|
||||
}
|
||||
@@ -449,7 +468,7 @@ public class ItemEventListener implements Listener {
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onEntityDamage(EntityDamageEvent event) {
|
||||
if (event.getEntity() instanceof org.bukkit.entity.Item item) {
|
||||
Optional.ofNullable(this.plugin.itemManager().wrap(item.getItemStack()))
|
||||
Optional.of(this.plugin.itemManager().wrap(item.getItemStack()))
|
||||
.flatMap(Item::getCustomItem)
|
||||
.ifPresent(it -> {
|
||||
if (it.settings().invulnerable().contains(DamageCauseUtils.fromBukkit(event.getCause()))) {
|
||||
@@ -459,6 +478,7 @@ public class ItemEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
// 禁止附魔
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onEnchant(PrepareItemEnchantEvent event) {
|
||||
ItemStack itemToEnchant = event.getItem();
|
||||
@@ -471,6 +491,7 @@ public class ItemEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义堆肥改了
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onCompost(CompostItemEvent event) {
|
||||
ItemStack itemToCompost = event.getItem();
|
||||
@@ -480,6 +501,7 @@ public class ItemEventListener implements Listener {
|
||||
event.setWillRaiseLevel(RandomUtils.generateRandomFloat(0, 1) < optionalCustomItem.get().settings().compostProbability());
|
||||
}
|
||||
|
||||
// 用于附魔台纠正
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getInventory() instanceof EnchantingInventory inventory)) return;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,87 +0,0 @@
|
||||
package net.momirealms.craftengine.bukkit.item.recipe;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.ItemManager;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
|
||||
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.CraftingInput;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.block.Crafter;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.CrafterCraftEvent;
|
||||
import org.bukkit.inventory.CraftingRecipe;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CrafterEventListener implements Listener {
|
||||
private final ItemManager<ItemStack> itemManager;
|
||||
private final BukkitRecipeManager recipeManager;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public CrafterEventListener(BukkitCraftEngine plugin, BukkitRecipeManager recipeManager, ItemManager<ItemStack> itemManager) {
|
||||
this.itemManager = itemManager;
|
||||
this.recipeManager = recipeManager;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onCrafting(CrafterCraftEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
CraftingRecipe recipe = event.getRecipe();
|
||||
if (!(event.getBlock().getState() instanceof Crafter crafter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value());
|
||||
boolean isCustom = this.recipeManager.isCustomRecipe(recipeId);
|
||||
|
||||
// Maybe it's recipe from other plugins, then we ignore it
|
||||
if (!isCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory inventory = crafter.getInventory();
|
||||
ItemStack[] ingredients = inventory.getStorageContents();
|
||||
|
||||
List<UniqueIdItem<ItemStack>> uniqueIdItems = new ArrayList<>();
|
||||
for (ItemStack itemStack : ingredients) {
|
||||
if (ItemStackUtils.isEmpty(itemStack)) {
|
||||
uniqueIdItems.add(this.itemManager.uniqueEmptyItem());
|
||||
} else {
|
||||
Item<ItemStack> wrappedItem = this.itemManager.wrap(itemStack);
|
||||
uniqueIdItems.add(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem));
|
||||
}
|
||||
}
|
||||
|
||||
CraftingInput<ItemStack> input;
|
||||
if (ingredients.length == 9) {
|
||||
input = CraftingInput.of(3, 3, uniqueIdItems);
|
||||
} else if (ingredients.length == 4) {
|
||||
input = CraftingInput.of(2, 2, uniqueIdItems);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPELESS, input);
|
||||
if (ceRecipe != null) {
|
||||
event.setResult(ceRecipe.assemble(input, ItemBuildContext.EMPTY));
|
||||
return;
|
||||
}
|
||||
ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input);
|
||||
if (ceRecipe != null) {
|
||||
event.setResult(ceRecipe.assemble(input, ItemBuildContext.EMPTY));
|
||||
return;
|
||||
}
|
||||
// clear result if not met
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,9 @@ import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector;
|
||||
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.MRecipeTypes;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.InventoryUtils;
|
||||
@@ -28,21 +25,14 @@ import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.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;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.*;
|
||||
import org.bukkit.event.inventory.*;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.*;
|
||||
import org.bukkit.inventory.view.AnvilView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -74,14 +64,14 @@ public class RecipeEventListener implements Listener {
|
||||
ItemStack item = event.getCurrentItem();
|
||||
if (ItemStackUtils.isEmpty(item)) return;
|
||||
if (ItemStackUtils.isEmpty(fuelStack)) {
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(getUniqueIdItem(item));
|
||||
Key recipeType;
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(ItemStackUtils.getUniqueIdItem(item));
|
||||
RecipeType recipeType;
|
||||
if (furnaceInventory.getType() == InventoryType.FURNACE) {
|
||||
recipeType = RecipeTypes.SMELTING;
|
||||
recipeType = RecipeType.SMELTING;
|
||||
} else if (furnaceInventory.getType() == InventoryType.BLAST_FURNACE) {
|
||||
recipeType = RecipeTypes.BLASTING;
|
||||
recipeType = RecipeType.BLASTING;
|
||||
} else {
|
||||
recipeType = RecipeTypes.SMOKING;
|
||||
recipeType = RecipeType.SMOKING;
|
||||
}
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(recipeType, input);
|
||||
@@ -258,166 +248,9 @@ public class RecipeEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onFurnaceInventoryOpen(InventoryOpenEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!(event.getInventory() instanceof FurnaceInventory furnaceInventory)) {
|
||||
return;
|
||||
}
|
||||
Furnace furnace = furnaceInventory.getHolder();
|
||||
try {
|
||||
Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace);
|
||||
RecipeInjector.injectCookingBlockEntity(blockEntity);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to inject cooking block entity", e);
|
||||
}
|
||||
}
|
||||
|
||||
// for 1.20.1-1.21.1
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onBlockIgnite(BlockIgniteEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (VersionHelper.isOrAbove1_21_2()) return;
|
||||
Block block = event.getBlock();
|
||||
Material material = block.getType();
|
||||
if (material == Material.CAMPFIRE) {
|
||||
if (block.getState() instanceof Campfire campfire) {
|
||||
try {
|
||||
Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire);
|
||||
RecipeInjector.injectCookingBlockEntity(blockEntity);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to inject cooking block entity", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlaceBlock(BlockPlaceEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
Block block = event.getBlock();
|
||||
Material material = block.getType();
|
||||
if (material == Material.FURNACE || material == Material.BLAST_FURNACE || material == Material.SMOKER) {
|
||||
if (block.getState() instanceof Furnace furnace) {
|
||||
try {
|
||||
Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace);
|
||||
RecipeInjector.injectCookingBlockEntity(blockEntity);
|
||||
} catch (Exception e) {
|
||||
plugin.logger().warn("Failed to inject cooking block entity", e);
|
||||
}
|
||||
}
|
||||
} else if (!VersionHelper.isOrAbove1_21_2() && material == Material.CAMPFIRE) {
|
||||
if (block.getState() instanceof Campfire campfire) {
|
||||
try {
|
||||
Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire);
|
||||
RecipeInjector.injectCookingBlockEntity(blockEntity);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to inject cooking block entity", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for 1.21.2+
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPutItemOnCampfire(PlayerInteractEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!VersionHelper.isOrAbove1_21_2()) return;
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
|
||||
Block clicked = event.getClickedBlock();
|
||||
if (clicked == null) return;
|
||||
Material type = clicked.getType();
|
||||
if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return;
|
||||
if (clicked.getState() instanceof Campfire campfire) {
|
||||
try {
|
||||
Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire);
|
||||
RecipeInjector.injectCookingBlockEntity(blockEntity);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to inject cooking block entity", e);
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack itemStack = event.getItem();
|
||||
if (ItemStackUtils.isEmpty(itemStack)) return;
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<Object> optionalMCRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(
|
||||
BukkitRecipeManager.nmsRecipeManager(),
|
||||
MRecipeTypes.CAMPFIRE_COOKING,
|
||||
CoreReflections.constructor$SingleRecipeInput.newInstance(FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)),
|
||||
FastNMS.INSTANCE.field$CraftWorld$ServerLevel(event.getPlayer().getWorld()),
|
||||
null
|
||||
);
|
||||
if (optionalMCRecipe.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(getUniqueIdItem(itemStack));
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
if (ceRecipe == null) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to handle interact campfire", e);
|
||||
}
|
||||
}
|
||||
|
||||
// for 1.21.2+
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onCampfireCook(CampfireStartEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!VersionHelper.isOrAbove1_21_2()) return;
|
||||
CampfireRecipe recipe = event.getRecipe();
|
||||
Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value());
|
||||
|
||||
boolean isCustom = this.recipeManager.isCustomRecipe(recipeId);
|
||||
if (!isCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack itemStack = event.getSource();
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(getUniqueIdItem(itemStack));
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
if (ceRecipe == null) {
|
||||
event.setTotalCookTime(Integer.MAX_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
event.setTotalCookTime(ceRecipe.cookingTime());
|
||||
}
|
||||
|
||||
// for 1.21.2+
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onCampfireCook(BlockCookEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!VersionHelper.isOrAbove1_21_2()) return;
|
||||
Material type = event.getBlock().getType();
|
||||
if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return;
|
||||
CampfireRecipe recipe = (CampfireRecipe) event.getRecipe();
|
||||
if (recipe == null) return;
|
||||
|
||||
Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value());
|
||||
|
||||
boolean isCustom = this.recipeManager.isCustomRecipe(recipeId);
|
||||
if (!isCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack itemStack = event.getSource();
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(getUniqueIdItem(itemStack));
|
||||
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input);
|
||||
if (ceRecipe == null) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
event.setResult(ceRecipe.result(ItemBuildContext.EMPTY));
|
||||
}
|
||||
|
||||
// Paper only
|
||||
@EventHandler
|
||||
public void onPrepareResult(PrepareResultEvent event) {
|
||||
// if (!ConfigManager.enableRecipeSystem()) return;
|
||||
if (event.getInventory() instanceof CartographyInventory cartographyInventory) {
|
||||
if (ItemStackUtils.hasCustomItem(cartographyInventory.getStorageContents())) {
|
||||
event.setResult(new ItemStack(Material.AIR));
|
||||
@@ -481,7 +314,7 @@ public class RecipeEventListener implements Listener {
|
||||
|
||||
// 如果禁止在铁砧使用两个相同物品修复
|
||||
firstCustom.ifPresent(it -> {
|
||||
if (!it.settings().canRepair()) {
|
||||
if (it.settings().canRepair() == Tristate.FALSE) {
|
||||
event.setResult(null);
|
||||
}
|
||||
});
|
||||
@@ -522,7 +355,7 @@ public class RecipeEventListener implements Listener {
|
||||
Key firstId = wrappedFirst.id();
|
||||
Optional<CustomItem<ItemStack>> optionalCustomTool = wrappedFirst.getCustomItem();
|
||||
// 物品无法被修复
|
||||
if (optionalCustomTool.isPresent() && !optionalCustomTool.get().settings().canRepair()) {
|
||||
if (optionalCustomTool.isPresent() && optionalCustomTool.get().settings().canRepair() == Tristate.FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -535,7 +368,7 @@ public class RecipeEventListener implements Listener {
|
||||
repairItem = item;
|
||||
break;
|
||||
}
|
||||
if (wrappedFirst.is(tag)) {
|
||||
if (wrappedFirst.hasItemTag(tag)) {
|
||||
repairItem = item;
|
||||
break;
|
||||
}
|
||||
@@ -679,89 +512,52 @@ public class RecipeEventListener implements Listener {
|
||||
}
|
||||
|
||||
// only handle repair items for the moment
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onSpecialRecipe(PrepareItemCraftEvent event) {
|
||||
// if (!ConfigManager.enableRecipeSystem()) return;
|
||||
org.bukkit.inventory.Recipe recipe = event.getRecipe();
|
||||
if (recipe == null)
|
||||
return;
|
||||
if (!(recipe instanceof ComplexRecipe complexRecipe))
|
||||
return;
|
||||
CraftingInventory inventory = event.getInventory();
|
||||
ItemStack result = inventory.getResult();
|
||||
if (ItemStackUtils.isEmpty(result))
|
||||
return;
|
||||
boolean hasCustomItem = ItemStackUtils.hasCustomItem(inventory.getMatrix());
|
||||
if (!hasCustomItem) {
|
||||
if (!hasCustomItem)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CraftBukkitReflections.clazz$CraftComplexRecipe.isInstance(complexRecipe)) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object mcRecipe = CraftBukkitReflections.field$CraftComplexRecipe$recipe.get(complexRecipe);
|
||||
|
||||
// Repair recipe
|
||||
if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe) || CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) {
|
||||
return;
|
||||
}
|
||||
// 处理修复配方,在此处理才能使用玩家参数构建物品
|
||||
if (CoreReflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) {
|
||||
// repair item
|
||||
ItemStack[] itemStacks = inventory.getMatrix();
|
||||
Pair<ItemStack, ItemStack> onlyTwoItems = getTheOnlyTwoItem(itemStacks);
|
||||
if (onlyTwoItems.left() == null || onlyTwoItems.right() == null) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Item<ItemStack> left = plugin.itemManager().wrap(onlyTwoItems.left());
|
||||
Item<ItemStack> right = plugin.itemManager().wrap(onlyTwoItems.right());
|
||||
if (!left.id().equals(right.id())) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
int totalDamage = right.damage().orElse(0) + left.damage().orElse(0);
|
||||
int totalMaxDamage = left.maxDamage() + right.maxDamage();
|
||||
// should be impossible, but take care
|
||||
if (totalDamage >= totalMaxDamage) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
|
||||
Optional<CustomItem<ItemStack>> customItemOptional = plugin.itemManager().getCustomItem(left.id());
|
||||
Pair<ItemStack, ItemStack> theOnlyTwoItem = getTheOnlyTwoItem(inventory.getMatrix());
|
||||
if (theOnlyTwoItem == null) return;
|
||||
Item<ItemStack> first = BukkitItemManager.instance().wrap(theOnlyTwoItem.left());
|
||||
Item<ItemStack> right = BukkitItemManager.instance().wrap(theOnlyTwoItem.right());
|
||||
int max = Math.max(first.maxDamage(), right.maxDamage());
|
||||
int durability1 = first.maxDamage() - first.damage().orElse(0);
|
||||
int durability2 = right.maxDamage() - right.damage().orElse(0);
|
||||
int finalDurability = durability1 + durability2 + max * 5 / 100;
|
||||
Optional<CustomItem<ItemStack>> customItemOptional = plugin.itemManager().getCustomItem(first.id());
|
||||
if (customItemOptional.isEmpty()) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
CustomItem<ItemStack> customItem = customItemOptional.get();
|
||||
if (!customItem.settings().canRepair()) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Item<ItemStack> newItem = customItem.buildItem(ItemBuildContext.of(plugin.adapt(player)));
|
||||
int remainingDurability = totalMaxDamage - totalDamage;
|
||||
int newItemDamage = Math.max(0, newItem.maxDamage() - remainingDurability);
|
||||
newItem.damage(newItemDamage);
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
Item<ItemStack> newItem = customItemOptional.get().buildItem(plugin.adapt(player));
|
||||
newItem.maxDamage(max);
|
||||
newItem.damage(Math.max(max - finalDurability, 0));
|
||||
inventory.setResult(newItem.getItem());
|
||||
} else if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe) || CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) {
|
||||
ItemStack[] itemStacks = inventory.getMatrix();
|
||||
for (ItemStack itemStack : itemStacks) {
|
||||
if (itemStack == null) continue;
|
||||
Item<ItemStack> item = plugin.itemManager().wrap(itemStack);
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
|
||||
if (optionalCustomItem.isPresent() && !optionalCustomItem.get().settings().dyeable()) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
// 其他配方不允许使用自定义物品
|
||||
inventory.setResult(null);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to handle minecraft custom recipe", e);
|
||||
this.plugin.logger().warn("Failed to handle custom recipe", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,134 +568,54 @@ public class RecipeEventListener implements Listener {
|
||||
if (itemStack == null) continue;
|
||||
if (first == null) {
|
||||
first = itemStack;
|
||||
} else if (second == null) {
|
||||
} else {
|
||||
if (second != null) {
|
||||
return null;
|
||||
}
|
||||
second = itemStack;
|
||||
}
|
||||
}
|
||||
return new Pair<>(first, second);
|
||||
}
|
||||
|
||||
// 不是完美的解决方案,仍然需要更多的探讨
|
||||
// TODO 生成类代理掉ResultSlot,并注入menu的slots对象,修改掉onTake方法
|
||||
// TODO 对于耐久度降低的配方,应该注册special recipe?
|
||||
@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 (ItemStackUtils.isEmpty(usedItem)) continue;
|
||||
if (usedItem.getAmount() != 1) continue;
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(usedItem);
|
||||
if (ItemUtils.isEmpty(wrapped)) 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;
|
||||
org.bukkit.inventory.Recipe recipe = event.getRecipe();
|
||||
if (recipe == null)
|
||||
return;
|
||||
|
||||
// we only handle shaped and shapeless recipes
|
||||
boolean shapeless = event.getRecipe() instanceof ShapelessRecipe;
|
||||
boolean shaped = event.getRecipe() instanceof ShapedRecipe;
|
||||
if (!shaped && !shapeless) return;
|
||||
|
||||
CraftingRecipe craftingRecipe = (CraftingRecipe) recipe;
|
||||
if (!(recipe instanceof CraftingRecipe craftingRecipe)) return;
|
||||
Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value());
|
||||
|
||||
boolean isCustom = this.recipeManager.isCustomRecipe(recipeId);
|
||||
// Maybe it's recipe from other plugins, then we ignore it
|
||||
if (!isCustom) {
|
||||
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
|
||||
// 也许是其他插件注册的配方,直接无视
|
||||
if (optionalRecipe.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CraftingInventory inventory = event.getInventory();
|
||||
ItemStack[] ingredients = inventory.getMatrix();
|
||||
if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
CraftingInput<ItemStack> input = getCraftingInput(inventory);
|
||||
if (input == null) return;
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
|
||||
}
|
||||
|
||||
private CraftingInput<ItemStack> getCraftingInput(CraftingInventory inventory) {
|
||||
ItemStack[] ingredients = inventory.getMatrix();
|
||||
List<UniqueIdItem<ItemStack>> uniqueIdItems = new ArrayList<>();
|
||||
for (ItemStack itemStack : ingredients) {
|
||||
uniqueIdItems.add(getUniqueIdItem(itemStack));
|
||||
uniqueIdItems.add(ItemStackUtils.getUniqueIdItem(itemStack));
|
||||
}
|
||||
|
||||
CraftingInput<ItemStack> input;
|
||||
if (ingredients.length == 9) {
|
||||
input = CraftingInput.of(3, 3, uniqueIdItems);
|
||||
} else if (ingredients.length == 4) {
|
||||
input = CraftingInput.of(2, 2, uniqueIdItems);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
Key lastRecipe = serverPlayer.lastUsedRecipe();
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPELESS, input, lastRecipe);
|
||||
if (ceRecipe != null) {
|
||||
inventory.setResult(ceRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
|
||||
serverPlayer.setLastUsedRecipe(ceRecipe.id());
|
||||
if (!ceRecipe.id().equals(recipeId)) {
|
||||
correctCraftingRecipeUsed(inventory, ceRecipe);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe);
|
||||
if (ceRecipe != null) {
|
||||
inventory.setResult(ceRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
|
||||
serverPlayer.setLastUsedRecipe(ceRecipe.id());
|
||||
if (!ceRecipe.id().equals(recipeId)) {
|
||||
correctCraftingRecipeUsed(inventory, ceRecipe);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// clear result if not met
|
||||
inventory.setResult(null);
|
||||
}
|
||||
|
||||
private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe<ItemStack> recipe) {
|
||||
Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
|
||||
if (holderOrRecipe == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object resultInventory = CraftBukkitReflections.field$CraftInventoryCrafting$resultInventory.get(inventory);
|
||||
CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to correct used recipe", e);
|
||||
return null;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
@@ -923,30 +639,21 @@ public class RecipeEventListener implements Listener {
|
||||
}
|
||||
|
||||
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value());
|
||||
boolean isCustom = this.recipeManager.isCustomRecipe(recipeId);
|
||||
// Maybe it's recipe from other plugins, then we ignore it
|
||||
if (!isCustom) {
|
||||
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
|
||||
if (optionalRecipe.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SmithingInput<ItemStack> input = new SmithingInput<>(
|
||||
getUniqueIdItem(inventory.getInputEquipment()),
|
||||
getUniqueIdItem(inventory.getInputTemplate()),
|
||||
getUniqueIdItem(inventory.getInputMineral())
|
||||
);
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SMITHING_TRIM, input);
|
||||
if (ceRecipe == null) {
|
||||
if (!(optionalRecipe.get() instanceof CustomSmithingTrimRecipe<ItemStack> smithingTrimRecipe)) {
|
||||
event.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
CustomSmithingTrimRecipe<ItemStack> trimRecipe = (CustomSmithingTrimRecipe<ItemStack>) ceRecipe;
|
||||
ItemStack result = trimRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY));
|
||||
event.setResult(result);
|
||||
if (!ceRecipe.id().equals(recipeId)) {
|
||||
correctSmithingRecipeUsed(inventory, ceRecipe);
|
||||
SmithingInput<ItemStack> input = getSmithingInput(inventory);
|
||||
if (smithingTrimRecipe.matches(input)) {
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY));
|
||||
event.setResult(result);
|
||||
} else {
|
||||
event.setResult(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,59 +662,30 @@ public class RecipeEventListener implements Listener {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
SmithingInventory inventory = event.getInventory();
|
||||
if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return;
|
||||
|
||||
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value());
|
||||
boolean isCustom = this.recipeManager.isCustomRecipe(recipeId);
|
||||
// Maybe it's recipe from other plugins, then we ignore it
|
||||
if (!isCustom) {
|
||||
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
|
||||
if (optionalRecipe.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack base = inventory.getInputEquipment();
|
||||
ItemStack template = inventory.getInputTemplate();
|
||||
ItemStack addition = inventory.getInputMineral();
|
||||
|
||||
SmithingInput<ItemStack> input = new SmithingInput<>(
|
||||
getUniqueIdItem(base),
|
||||
getUniqueIdItem(template),
|
||||
getUniqueIdItem(addition)
|
||||
);
|
||||
|
||||
Recipe<ItemStack> ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SMITHING_TRANSFORM, input);
|
||||
if (ceRecipe == null) {
|
||||
if (!(optionalRecipe.get() instanceof CustomSmithingTransformRecipe<ItemStack> smithingTransformRecipe)) {
|
||||
event.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
|
||||
CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe;
|
||||
ItemStack processed = transformRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY));
|
||||
event.setResult(processed);
|
||||
if (!ceRecipe.id().equals(recipeId)) {
|
||||
correctSmithingRecipeUsed(inventory, ceRecipe);
|
||||
}
|
||||
}
|
||||
|
||||
private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) {
|
||||
Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
|
||||
if (holderOrRecipe == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory);
|
||||
CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to correct used recipe", e);
|
||||
}
|
||||
}
|
||||
|
||||
private UniqueIdItem<ItemStack> getUniqueIdItem(@Nullable ItemStack itemStack) {
|
||||
if (ItemStackUtils.isEmpty(itemStack)) {
|
||||
return this.itemManager.uniqueEmptyItem();
|
||||
SmithingInput<ItemStack> input = getSmithingInput(inventory);
|
||||
if (smithingTransformRecipe.matches(input)) {
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
ItemStack processed = smithingTransformRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY));
|
||||
event.setResult(processed);
|
||||
} else {
|
||||
Item<ItemStack> wrappedItem = this.itemManager.wrap(itemStack);
|
||||
return new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem);
|
||||
event.setResult(null);
|
||||
}
|
||||
}
|
||||
|
||||
private SmithingInput<ItemStack> getSmithingInput(SmithingInventory inventory) {
|
||||
return new SmithingInput<>(
|
||||
ItemStackUtils.getUniqueIdItem(inventory.getInputEquipment()),
|
||||
ItemStackUtils.getUniqueIdItem(inventory.getInputTemplate()),
|
||||
ItemStackUtils.getUniqueIdItem(inventory.getInputMineral())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.pack;
|
||||
import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackGenerateEvent;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.feature.ReloadCommand;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.ResourcePackUtils;
|
||||
@@ -22,7 +21,6 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
@@ -60,28 +58,12 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG) {
|
||||
if (VersionHelper.isOrAbove1_20_2()) {
|
||||
this.resetServerSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
HandlerList.unregisterAll(this);
|
||||
this.resetServerSettings();
|
||||
}
|
||||
|
||||
public void resetServerSettings() {
|
||||
try {
|
||||
Object settings = CoreReflections.field$DedicatedServer$settings.get(CoreReflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = CoreReflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
CoreReflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.empty());
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to reset resource pack settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
|
||||
@@ -227,7 +227,6 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
}
|
||||
}, 1, 1);
|
||||
}
|
||||
super.compatibilityManager().onDelayedEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.plugin.classpath;
|
||||
import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender;
|
||||
import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -17,27 +18,38 @@ public class PaperClassPathAppender implements ClassPathAppender {
|
||||
public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader)
|
||||
.map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0))
|
||||
.orElse(null);
|
||||
private final URLClassLoaderAccess classLoaderAccess;
|
||||
private final URLClassLoaderAccess libraryClassLoaderAccess;
|
||||
|
||||
// todo 是否有更好的方法让库被其他插件共享
|
||||
public PaperClassPathAppender(ClassLoader classLoader) {
|
||||
try {
|
||||
if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) {
|
||||
URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader);
|
||||
this.classLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader);
|
||||
} else if (classLoader instanceof URLClassLoader) {
|
||||
this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader);
|
||||
} else {
|
||||
throw new IllegalStateException("ClassLoader is not instance of URLClassLoader");
|
||||
// 这个类加载器用于加载重定位后的依赖库,这样所有插件都能访问到
|
||||
ClassLoader bukkitClassLoader = Bukkit.class.getClassLoader();
|
||||
if (bukkitClassLoader instanceof URLClassLoader urlClassLoader) {
|
||||
this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader);
|
||||
} else {
|
||||
// ignite会把Bukkit放置于EmberClassLoader中,获取其父DynamicClassLoader
|
||||
if (bukkitClassLoader.getClass().getName().equals("space.vectrix.ignite.launch.ember.EmberClassLoader") && bukkitClassLoader.getParent() instanceof URLClassLoader urlClassLoader) {
|
||||
this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 最次的方案,使用paper自带的classloader去加载依赖,这种情况会发生依赖隔离
|
||||
if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) {
|
||||
URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader);
|
||||
this.libraryClassLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader);
|
||||
} else {
|
||||
throw new IllegalStateException("ClassLoader is not instance of URLClassLoader");
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJarToClasspath(Path file) {
|
||||
try {
|
||||
this.classLoaderAccess.addURL(file.toUri().toURL());
|
||||
this.libraryClassLoaderAccess.addURL(file.toUri().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class BukkitSenderFactory extends SenderFactory<BukkitCraftEngine, Comman
|
||||
if (sender instanceof Player player) {
|
||||
FastNMS.INSTANCE.method$Connection$send(
|
||||
FastNMS.INSTANCE.field$ServerGamePacketListenerImpl$connection(FastNMS.INSTANCE.field$Player$connection(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player))),
|
||||
FastNMS.INSTANCE.constructor$ClientboundSystemChatPacket(ComponentUtils.adventureToMinecraft(message), false));
|
||||
FastNMS.INSTANCE.constructor$ClientboundSystemChatPacket(ComponentUtils.adventureToMinecraft(message), false), null);
|
||||
} else if (sender instanceof ConsoleCommandSender commandSender) {
|
||||
commandSender.sendMessage(LegacyComponentSerializer.legacySection().serialize(message));
|
||||
} else if (sender instanceof RemoteConsoleCommandSender commandSender) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -74,9 +75,17 @@ public class GiveItemCommand extends BukkitCommandFeature<CommandSender> {
|
||||
ItemStack more = builtItem.clone();
|
||||
more.setAmount(perStackSize);
|
||||
if (toInv) {
|
||||
PlayerUtils.putItemsToInventory(player.getInventory(), more, more.getAmount());
|
||||
if (VersionHelper.isFolia()) {
|
||||
player.getScheduler().run(plugin().javaPlugin(), (t) -> PlayerUtils.putItemsToInventory(player.getInventory(), more, more.getAmount()), () -> {});
|
||||
} else {
|
||||
PlayerUtils.putItemsToInventory(player.getInventory(), more, more.getAmount());
|
||||
}
|
||||
} else {
|
||||
PlayerUtils.dropItem(player, more, false, true, false);
|
||||
if (VersionHelper.isFolia()) {
|
||||
player.getScheduler().run(plugin().javaPlugin(), (t) -> PlayerUtils.dropItem(player, more, false, true, false), () -> {});
|
||||
} else {
|
||||
PlayerUtils.dropItem(player, more, false, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.bukkit.parser.location.LocationParser;
|
||||
import org.incendo.cloud.parser.standard.IntegerParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
@@ -29,35 +18,9 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.senderType(Player.class)
|
||||
.required("location", LocationParser.locationParser())
|
||||
.required("remove", IntegerParser.integerParser())
|
||||
.handler(context -> {
|
||||
Player player = context.sender();
|
||||
int removeEntityId = context.get("remove");
|
||||
if (removeEntityId >= 0) {
|
||||
try {
|
||||
Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{removeEntityId});
|
||||
plugin().adapt(player).sendPacket(packet, true);
|
||||
player.sendMessage("发送成功");
|
||||
} catch (ReflectiveOperationException e) {
|
||||
player.sendMessage("发送失败");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Location location = context.get("location");
|
||||
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||
List<Object> packets = new ArrayList<>();
|
||||
List<Object> cachedShulkerValues = new ArrayList<>();
|
||||
HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, cachedShulkerValues); // NO AI
|
||||
// HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedShulkerValues); // Invisible
|
||||
HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(true, cachedShulkerValues);
|
||||
packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityId, UUID.randomUUID(), location.x(), location.y(), location.z(), 0, location.getYaw(),
|
||||
MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||
));
|
||||
packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, List.copyOf(cachedShulkerValues)));
|
||||
plugin().adapt(player).sendPackets(packets, true);
|
||||
player.sendMessage("发送成功 id: " + entityId);
|
||||
player.sendMessage("客户端模组状态: " + BukkitNetworkManager.instance().getUser(player).clientModEnabled());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public final class BlockStateGenerator {
|
||||
if (vec3 == null) return List.of();
|
||||
|
||||
Object tool = FastNMS.INSTANCE.method$LootParams$Builder$getOptionalParameter(builder, MLootContextParams.TOOL);
|
||||
Item<ItemStack> item = BukkitItemManager.instance().wrap(tool == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(tool) ? null : FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(tool));
|
||||
Item<ItemStack> item = BukkitItemManager.instance().wrap(tool == null ? null : FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(tool));
|
||||
Object optionalPlayer = FastNMS.INSTANCE.method$LootParams$Builder$getOptionalParameter(builder, MLootContextParams.THIS_ENTITY);
|
||||
if (!CoreReflections.clazz$Player.isInstance(optionalPlayer)) {
|
||||
optionalPlayer = null;
|
||||
@@ -107,7 +107,7 @@ public final class BlockStateGenerator {
|
||||
// do not drop if it's not the correct tool
|
||||
BlockSettings settings = state.settings();
|
||||
if (optionalPlayer != null && settings.requireCorrectTool()) {
|
||||
if (item == null) return List.of();
|
||||
if (item.isEmpty()) return List.of();
|
||||
if (!settings.isCorrectTool(item.id()) &&
|
||||
(!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool, state.customBlockState().handle()))) {
|
||||
return List.of();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.injector;
|
||||
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeType;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public interface InjectedCacheCheck {
|
||||
@@ -8,9 +9,9 @@ public interface InjectedCacheCheck {
|
||||
|
||||
void recipeType(Object recipeType);
|
||||
|
||||
Key customRecipeType();
|
||||
RecipeType customRecipeType();
|
||||
|
||||
void customRecipeType(Key customRecipeType);
|
||||
void customRecipeType(RecipeType customRecipeType);
|
||||
|
||||
Object lastRecipe();
|
||||
|
||||
|
||||
@@ -1,284 +1,405 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.injector;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.ClassFileVersion;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.bind.annotation.AllArguments;
|
||||
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||
import net.bytebuddy.implementation.bind.annotation.This;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
|
||||
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.MRecipeTypes;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
|
||||
import net.momirealms.craftengine.bukkit.util.ItemTags;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
|
||||
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class RecipeInjector {
|
||||
private static Class<?> clazz$InjectedCacheChecker;
|
||||
|
||||
private static Class<?> clazz$InjectedArmorDyeRecipe;
|
||||
private static Class<?> clazz$InjectedRepairItemRecipe;
|
||||
private static Class<?> clazz$InjectedFireworkStarFadeRecipe;
|
||||
|
||||
public static void init() {
|
||||
ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17);
|
||||
clazz$InjectedCacheChecker = byteBuddy
|
||||
.subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
|
||||
.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"))
|
||||
ElementMatcher.Junction<MethodDescription> matches = (VersionHelper.isOrAbove1_21() ?
|
||||
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$Level) :
|
||||
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$Level)
|
||||
).and(ElementMatchers.returns(boolean.class));
|
||||
ElementMatcher.Junction<MethodDescription> assemble = (
|
||||
VersionHelper.isOrAbove1_21() ?
|
||||
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$HolderLookup$Provider) :
|
||||
VersionHelper.isOrAbove1_20_5() ?
|
||||
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$HolderLookup$Provider) :
|
||||
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$RegistryAccess)
|
||||
).and(ElementMatchers.returns(CoreReflections.clazz$ItemStack));
|
||||
|
||||
.defineField("customRecipeType", Key.class, Visibility.PUBLIC)
|
||||
.method(ElementMatchers.named("customRecipeType"))
|
||||
.intercept(FieldAccessor.ofField("customRecipeType"))
|
||||
clazz$InjectedArmorDyeRecipe = byteBuddy
|
||||
.subclass(CoreReflections.clazz$ArmorDyeRecipe, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
|
||||
.name("net.momirealms.craftengine.bukkit.item.recipe.ArmorDyeRecipe")
|
||||
.method(matches)
|
||||
.intercept(MethodDelegation.to(DyeMatchesInterceptor.INSTANCE))
|
||||
.method(assemble)
|
||||
.intercept(MethodDelegation.to(DyeAssembleInterceptor.INSTANCE))
|
||||
.make()
|
||||
.load(RecipeInjector.class.getClassLoader())
|
||||
.getLoaded();
|
||||
|
||||
.defineField("lastRecipe", Object.class, Visibility.PUBLIC)
|
||||
.method(ElementMatchers.named("lastRecipe"))
|
||||
.intercept(FieldAccessor.ofField("lastRecipe"))
|
||||
clazz$InjectedFireworkStarFadeRecipe = byteBuddy
|
||||
.subclass(CoreReflections.clazz$FireworkStarFadeRecipe)
|
||||
.name("net.momirealms.craftengine.bukkit.item.recipe.FireworkStarFadeRecipe")
|
||||
.method(matches)
|
||||
.intercept(MethodDelegation.to(FireworkStarFadeMatchesInterceptor.INSTANCE))
|
||||
.method(assemble)
|
||||
.intercept(MethodDelegation.to(FireworkStarFadeAssembleInterceptor.INSTANCE))
|
||||
.make()
|
||||
.load(RecipeInjector.class.getClassLoader())
|
||||
.getLoaded();
|
||||
|
||||
.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() ?
|
||||
GetRecipeForMethodInterceptor1_21_2.INSTANCE :
|
||||
(VersionHelper.isOrAbove1_21() ?
|
||||
GetRecipeForMethodInterceptor1_21.INSTANCE :
|
||||
VersionHelper.isOrAbove1_20_5() ?
|
||||
GetRecipeForMethodInterceptor1_20_5.INSTANCE :
|
||||
GetRecipeForMethodInterceptor1_20.INSTANCE)
|
||||
))
|
||||
clazz$InjectedRepairItemRecipe = byteBuddy
|
||||
.subclass(CoreReflections.clazz$RepairItemRecipe, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
|
||||
.name("net.momirealms.craftengine.bukkit.item.recipe.RepairItemRecipe")
|
||||
// 只修改match逻辑,合并需要在事件里处理,否则无法应用变量
|
||||
.method(matches)
|
||||
.intercept(MethodDelegation.to(RepairMatchesInterceptor.INSTANCE))
|
||||
.make()
|
||||
.load(RecipeInjector.class.getClassLoader())
|
||||
.getLoaded();
|
||||
}
|
||||
|
||||
public static void injectCookingBlockEntity(Object entity) throws ReflectiveOperationException {
|
||||
if (CoreReflections.clazz$AbstractFurnaceBlockEntity.isInstance(entity)) {
|
||||
Object quickCheck = CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.get(entity);
|
||||
if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected
|
||||
Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity);
|
||||
InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker);
|
||||
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);
|
||||
public static Object createRepairItemRecipe(Key id) throws ReflectiveOperationException {
|
||||
return createSpecialRecipe(id, clazz$InjectedRepairItemRecipe);
|
||||
}
|
||||
|
||||
public static Object createCustomDyeRecipe(Key id) throws ReflectiveOperationException {
|
||||
return createSpecialRecipe(id, clazz$InjectedArmorDyeRecipe);
|
||||
}
|
||||
|
||||
public static Object createFireworkStarFadeRecipe(Key id) throws ReflectiveOperationException {
|
||||
return createSpecialRecipe(id, clazz$InjectedFireworkStarFadeRecipe);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Object createSpecialRecipe(Key id, Class<?> clazz$InjectedRepairItemRecipe) throws InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException {
|
||||
if (VersionHelper.isOrAbove1_20_2()) {
|
||||
Constructor<?> constructor = ReflectionUtils.getConstructor(clazz$InjectedRepairItemRecipe, CoreReflections.clazz$CraftingBookCategory);
|
||||
return constructor.newInstance(CoreReflections.instance$CraftingBookCategory$MISC);
|
||||
} else {
|
||||
Constructor<?> constructor = ReflectionUtils.getConstructor(clazz$InjectedRepairItemRecipe, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$CraftingBookCategory);
|
||||
return constructor.newInstance(KeyUtils.toResourceLocation(id), CoreReflections.instance$CraftingBookCategory$MISC);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Function<Object, Integer> INGREDIENT_SIZE_GETTER =
|
||||
VersionHelper.isOrAbove1_21() ?
|
||||
FastNMS.INSTANCE::method$CraftingInput$size :
|
||||
FastNMS.INSTANCE::method$Container$getContainerSize;
|
||||
private static final BiFunction<Object, Integer, Object> INGREDIENT_GETTER =
|
||||
VersionHelper.isOrAbove1_21() ?
|
||||
FastNMS.INSTANCE::method$CraftingInput$getItem :
|
||||
FastNMS.INSTANCE::method$Container$getItem;
|
||||
|
||||
private static final Function<Object, Boolean> REPAIR_INGREDIENT_COUNT_CHECKER =
|
||||
VersionHelper.isOrAbove1_21() ?
|
||||
(input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) != 2 :
|
||||
(container) -> false;
|
||||
|
||||
public static class FireworkStarFadeMatchesInterceptor {
|
||||
public static final FireworkStarFadeMatchesInterceptor INSTANCE = new FireworkStarFadeMatchesInterceptor();
|
||||
|
||||
@RuntimeType
|
||||
public Object intercept(@AllArguments Object[] args) {
|
||||
Object input = args[0];
|
||||
if (DYE_INGREDIENT_COUNT_CHECKER.apply(input)) {
|
||||
return false;
|
||||
}
|
||||
boolean hasDye = false;
|
||||
boolean hasFireworkStar = false;
|
||||
int size = INGREDIENT_SIZE_GETTER.apply(input);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object itemStack = INGREDIENT_GETTER.apply(input, i);
|
||||
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
|
||||
if (isFireworkDye(wrapped)) {
|
||||
hasDye = true;
|
||||
} else {
|
||||
if (!wrapped.id().equals(ItemKeys.FIREWORK_STAR)) {
|
||||
return false;
|
||||
}
|
||||
if (hasFireworkStar) {
|
||||
return false;
|
||||
}
|
||||
hasFireworkStar = true;
|
||||
}
|
||||
}
|
||||
return hasDye && hasFireworkStar;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FireworkStarFadeAssembleInterceptor {
|
||||
public static final FireworkStarFadeAssembleInterceptor INSTANCE = new FireworkStarFadeAssembleInterceptor();
|
||||
|
||||
@RuntimeType
|
||||
public Object intercept(@AllArguments Object[] args) {
|
||||
IntList colors = new IntArrayList();
|
||||
Item<ItemStack> starItem = null;
|
||||
Object input = args[0];
|
||||
int size = INGREDIENT_SIZE_GETTER.apply(input);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object itemStack = INGREDIENT_GETTER.apply(input, i);
|
||||
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
|
||||
if (isFireworkDye(wrapped)) {
|
||||
Color color = getFireworkColor(wrapped);
|
||||
if (color == null) {
|
||||
return CoreReflections.instance$ItemStack$EMPTY;
|
||||
}
|
||||
colors.add(color.color());
|
||||
} else if (wrapped.id().equals(ItemKeys.FIREWORK_STAR)) {
|
||||
starItem = wrapped.copyWithCount(1);
|
||||
}
|
||||
}
|
||||
if (starItem == null || colors.isEmpty()) {
|
||||
return CoreReflections.instance$ItemStack$EMPTY;
|
||||
}
|
||||
FireworkExplosion explosion = starItem.fireworkExplosion().orElse(FireworkExplosion.DEFAULT);
|
||||
starItem.fireworkExplosion(explosion.withFadeColors(colors));
|
||||
return starItem.getLiteralObject();
|
||||
}
|
||||
}
|
||||
|
||||
public static class RepairMatchesInterceptor {
|
||||
public static final RepairMatchesInterceptor INSTANCE = new RepairMatchesInterceptor();
|
||||
|
||||
@RuntimeType
|
||||
public Object intercept(@AllArguments Object[] args) {
|
||||
Object input = args[0];
|
||||
if (REPAIR_INGREDIENT_COUNT_CHECKER.apply(input)) {
|
||||
return false;
|
||||
}
|
||||
return getItemsToCombine(input) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pair<Item<ItemStack>, Item<ItemStack>> getItemsToCombine(Object input) {
|
||||
Item<ItemStack> item1 = null;
|
||||
Item<ItemStack> item2 = null;
|
||||
int size = INGREDIENT_SIZE_GETTER.apply(input);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object itemStack = INGREDIENT_GETTER.apply(input, i);
|
||||
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
|
||||
if (item1 == null) {
|
||||
item1 = wrapped;
|
||||
} else {
|
||||
throw new IllegalStateException("RecipeType " + recipeType + " not supported");
|
||||
if (item2 != null) {
|
||||
return null;
|
||||
}
|
||||
item2 = wrapped;
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (item1 == null || item2 == null) {
|
||||
return null;
|
||||
}
|
||||
if (!canCombine(item1, item2)) {
|
||||
return null;
|
||||
}
|
||||
return new Pair<>(item1, item2);
|
||||
}
|
||||
|
||||
private static boolean canCombine(Item<ItemStack> input1, Item<ItemStack> input2) {
|
||||
if (input1.count() != 1 || !isDamageableItem(input1)) return false;
|
||||
if (input2.count() != 1 || !isDamageableItem(input2)) return false;
|
||||
if (!input1.id().equals(input2.id())) return false;
|
||||
Optional<CustomItem<ItemStack>> customItem = input1.getCustomItem();
|
||||
return customItem.isEmpty() || customItem.get().settings().canRepair() != Tristate.FALSE;
|
||||
}
|
||||
|
||||
private static boolean isDamageableItem(Item<ItemStack> item) {
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
return item.hasComponent(ComponentTypes.MAX_DAMAGE) && item.hasComponent(ComponentTypes.DAMAGE);
|
||||
} else {
|
||||
return FastNMS.INSTANCE.method$Item$canBeDepleted(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject()));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public static class GetRecipeForMethodInterceptor1_20 {
|
||||
public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20();
|
||||
private static final Function<Object, Boolean> DYE_INGREDIENT_COUNT_CHECKER =
|
||||
VersionHelper.isOrAbove1_21() ?
|
||||
(input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) < 2 :
|
||||
(container) -> false;
|
||||
|
||||
public static class DyeMatchesInterceptor {
|
||||
public static final DyeMatchesInterceptor INSTANCE = new DyeMatchesInterceptor();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@RuntimeType
|
||||
public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
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();
|
||||
public Object intercept(@AllArguments Object[] args) {
|
||||
Object input = args[0];
|
||||
if (DYE_INGREDIENT_COUNT_CHECKER.apply(input)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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());
|
||||
int size = INGREDIENT_SIZE_GETTER.apply(input);
|
||||
Item<ItemStack> itemToDye = null;
|
||||
boolean hasDye = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object itemStack = INGREDIENT_GETTER.apply(input, i);
|
||||
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
|
||||
if (isDyeable(wrapped)) {
|
||||
if (itemToDye != null) {
|
||||
return false;
|
||||
}
|
||||
itemToDye = wrapped;
|
||||
} else {
|
||||
if (!isArmorDye(wrapped)) {
|
||||
return false;
|
||||
}
|
||||
hasDye = true;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem));
|
||||
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));
|
||||
return hasDye && itemToDye != null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public static class GetRecipeForMethodInterceptor1_20_5 {
|
||||
public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5();
|
||||
public static class DyeAssembleInterceptor {
|
||||
public static final DyeAssembleInterceptor INSTANCE = new DyeAssembleInterceptor();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@RuntimeType
|
||||
public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
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();
|
||||
public Object intercept(@AllArguments Object[] args) {
|
||||
List<Color> colors = new ArrayList<>();
|
||||
Item<ItemStack> itemToDye = null;
|
||||
Object input = args[0];
|
||||
int size = INGREDIENT_SIZE_GETTER.apply(input);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object itemStack = INGREDIENT_GETTER.apply(input, i);
|
||||
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
|
||||
if (isDyeable(wrapped)) {
|
||||
itemToDye = wrapped.copyWithCount(1);
|
||||
} else {
|
||||
Color dyeColor = getDyeColor(wrapped);
|
||||
if (dyeColor != null) {
|
||||
colors.add(dyeColor);
|
||||
} else {
|
||||
return CoreReflections.instance$ItemStack$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;
|
||||
if (itemToDye == null || itemToDye.isEmpty() || colors.isEmpty()) {
|
||||
return CoreReflections.instance$ItemStack$EMPTY;
|
||||
}
|
||||
|
||||
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem));
|
||||
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));
|
||||
return itemToDye.applyDyedColors(colors).getLiteralObject();
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
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);
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem));
|
||||
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));
|
||||
@Nullable
|
||||
private static Color getDyeColor(final Item<ItemStack> dyeItem) {
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
|
||||
if (optionalCustomItem.isPresent()) {
|
||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||
return Optional.ofNullable(customItem.settings().dyeColor()).orElseGet(() -> getVanillaDyeColor(dyeItem));
|
||||
}
|
||||
return getVanillaDyeColor(dyeItem);
|
||||
}
|
||||
|
||||
@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) {
|
||||
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
|
||||
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);
|
||||
SingleItemInput<ItemStack> input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem));
|
||||
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));
|
||||
@Nullable
|
||||
private static Color getFireworkColor(final Item<ItemStack> dyeItem) {
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
|
||||
if (optionalCustomItem.isPresent()) {
|
||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||
return Optional.ofNullable(customItem.settings().fireworkColor()).orElseGet(() -> getVanillaFireworkColor(dyeItem));
|
||||
}
|
||||
return getVanillaFireworkColor(dyeItem);
|
||||
}
|
||||
|
||||
private static final Predicate<Item<ItemStack>> IS_DYEABLE =
|
||||
VersionHelper.isOrAbove1_20_5() ?
|
||||
(item -> item.hasItemTag(ItemTags.DYEABLE)) :
|
||||
(item -> {
|
||||
Object itemLike = FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject());
|
||||
return CoreReflections.clazz$DyeableLeatherItem.isInstance(itemLike);
|
||||
});
|
||||
|
||||
private static boolean isDyeable(final Item<ItemStack> item) {
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
|
||||
if (optionalCustomItem.isPresent()) {
|
||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||
if (customItem.settings().dyeable() == Tristate.FALSE) {
|
||||
return false;
|
||||
}
|
||||
if (customItem.settings().dyeable() == Tristate.TRUE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return IS_DYEABLE.test(item);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Color getVanillaDyeColor(final Item<ItemStack> item) {
|
||||
Object itemStack = item.getLiteralObject();
|
||||
Object dyeItem = FastNMS.INSTANCE.method$ItemStack$getItem(itemStack);
|
||||
if (!CoreReflections.clazz$DyeItem.isInstance(dyeItem)) return null;
|
||||
return Color.fromDecimal(FastNMS.INSTANCE.method$DyeColor$getTextureDiffuseColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Color getVanillaFireworkColor(final Item<ItemStack> item) {
|
||||
Object itemStack = item.getLiteralObject();
|
||||
Object dyeItem = FastNMS.INSTANCE.method$ItemStack$getItem(itemStack);
|
||||
if (!CoreReflections.clazz$DyeItem.isInstance(dyeItem)) return null;
|
||||
return Color.fromDecimal(FastNMS.INSTANCE.method$DyeColor$getFireworkColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem)));
|
||||
}
|
||||
|
||||
private static boolean isArmorDye(Item<ItemStack> dyeItem) {
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
|
||||
if (optionalCustomItem.isPresent()) {
|
||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||
return customItem.settings().dyeColor() != null || isVanillaDyeItem(dyeItem);
|
||||
}
|
||||
return isVanillaDyeItem(dyeItem);
|
||||
}
|
||||
|
||||
private static boolean isFireworkDye(Item<ItemStack> dyeItem) {
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
|
||||
if (optionalCustomItem.isPresent()) {
|
||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||
return customItem.settings().fireworkColor() != null || isVanillaDyeItem(dyeItem);
|
||||
}
|
||||
return isVanillaDyeItem(dyeItem);
|
||||
}
|
||||
|
||||
private static boolean isVanillaDyeItem(Item<ItemStack> item) {
|
||||
return CoreReflections.clazz$DyeItem.isInstance(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +67,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS[id] = function;
|
||||
}
|
||||
|
||||
private final BiConsumer<ChannelHandler, Object> packetConsumer;
|
||||
private final BiConsumer<ChannelHandler, List<Object>> packetsConsumer;
|
||||
private final BiConsumer<Channel, Object> immediatePacketConsumer;
|
||||
private final BiConsumer<Channel, List<Object>> immediatePacketsConsumer;
|
||||
private final TriConsumer<ChannelHandler, Object, Object> packetConsumer;
|
||||
private final TriConsumer<ChannelHandler, List<Object>, Object> packetsConsumer;
|
||||
private final TriConsumer<Channel, Object, Runnable> immediatePacketConsumer;
|
||||
private final TriConsumer<Channel, List<Object>, Runnable> immediatePacketsConsumer;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
private final Map<ChannelPipeline, BukkitServerPlayer> users = new ConcurrentHashMap<>();
|
||||
@@ -105,21 +105,30 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
this.registerPacketHandlers();
|
||||
// set up packet senders
|
||||
this.packetConsumer = FastNMS.INSTANCE::method$Connection$send;
|
||||
this.packetsConsumer = ((connection, packets) -> {
|
||||
this.packetsConsumer = ((connection, packets, sendListener) -> {
|
||||
Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||
this.packetConsumer.accept(connection, bundle);
|
||||
this.packetConsumer.accept(connection, bundle, sendListener);
|
||||
});
|
||||
this.immediatePacketConsumer = ChannelOutboundInvoker::writeAndFlush;
|
||||
this.immediatePacketsConsumer = (channel, packets) -> {
|
||||
this.immediatePacketConsumer = (channel, packet, sendListener) -> {
|
||||
ChannelFuture future = channel.writeAndFlush(packet);
|
||||
if (sendListener == null) return;
|
||||
future.addListener((ChannelFutureListener) channelFuture -> {
|
||||
sendListener.run();
|
||||
if (!channelFuture.isSuccess()) {
|
||||
channelFuture.channel().pipeline().fireExceptionCaught(channelFuture.cause());
|
||||
}
|
||||
});
|
||||
};
|
||||
this.immediatePacketsConsumer = (channel, packets, sendListener) -> {
|
||||
Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||
this.immediatePacketConsumer.accept(channel, bundle);
|
||||
this.immediatePacketConsumer.accept(channel, bundle, sendListener);
|
||||
};
|
||||
// set up mod channel
|
||||
this.plugin.javaPlugin().getServer().getMessenger().registerIncomingPluginChannel(this.plugin.javaPlugin(), MOD_CHANNEL, this);
|
||||
this.plugin.javaPlugin().getServer().getMessenger().registerOutgoingPluginChannel(this.plugin.javaPlugin(), MOD_CHANNEL);
|
||||
// Inject server channel
|
||||
try {
|
||||
Object server = CoreReflections.method$MinecraftServer$getServer.invoke(null);
|
||||
Object server = FastNMS.INSTANCE.method$MinecraftServer$getServer();
|
||||
Object serverConnection = CoreReflections.field$MinecraftServer$connection.get(server);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ChannelFuture> channels = (List<ChannelFuture>) CoreReflections.field$ServerConnectionListener$channels.get(serverConnection);
|
||||
@@ -127,7 +136,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
Channel channel = future.channel();
|
||||
injectServerChannel(channel);
|
||||
this.injectedChannels.add(channel);
|
||||
}, (object) -> {});
|
||||
}, (object) -> {
|
||||
});
|
||||
CoreReflections.field$ServerConnectionListener$channels.set(serverConnection, monitor);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to init server connection", e);
|
||||
@@ -188,7 +198,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
registerS2CByteBufPacketConsumer(PacketConsumers.SET_EQUIPMENT, this.packetIds.clientboundSetEquipmentPacket());
|
||||
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(VersionHelper.isOrAbove1_21_5() ? PacketConsumers.CONTAINER_CLICK_1_21_5 : PacketConsumers.CONTAINER_CLICK_1_20, this.packetIds.serverboundContainerClickPacket());
|
||||
registerC2SByteBufPacketConsumer(PacketConsumers.INTERACT_ENTITY, this.packetIds.serverboundInteractPacket());
|
||||
}
|
||||
|
||||
@@ -206,7 +216,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
this.resetUserArray();
|
||||
if (VersionHelper.isFolia()) {
|
||||
player.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> user.tick(),
|
||||
() -> {}, 1, 1);
|
||||
() -> {
|
||||
}, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,20 +316,20 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately) {
|
||||
public void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately, Runnable sendListener) {
|
||||
if (immediately) {
|
||||
this.immediatePacketConsumer.accept(player.nettyChannel(), packet);
|
||||
this.immediatePacketConsumer.accept(player.nettyChannel(), packet, sendListener);
|
||||
} else {
|
||||
this.packetConsumer.accept(player.connection(), packet);
|
||||
this.packetConsumer.accept(player.connection(), packet, sendListener != null ? FastNMS.INSTANCE.method$PacketSendListener$thenRun(sendListener) : null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPackets(@NotNull NetWorkUser player, List<Object> packet, boolean immediately) {
|
||||
public void sendPackets(@NotNull NetWorkUser player, List<Object> packet, boolean immediately, Runnable sendListener) {
|
||||
if (immediately) {
|
||||
this.immediatePacketsConsumer.accept(player.nettyChannel(), packet);
|
||||
this.immediatePacketsConsumer.accept(player.nettyChannel(), packet, sendListener);
|
||||
} else {
|
||||
this.packetsConsumer.accept(player.connection(), packet);
|
||||
this.packetsConsumer.accept(player.connection(), packet, sendListener != null ? FastNMS.INSTANCE.method$PacketSendListener$thenRun(sendListener) : null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +696,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
ByteBuf temp = ctx.alloc().buffer();
|
||||
try {
|
||||
if (compressor != null) {
|
||||
callEncode(compressor, ctx, input, temp);
|
||||
callEncode(compressor, ctx, input, temp);
|
||||
}
|
||||
} finally {
|
||||
input.clear().writeBytes(temp);
|
||||
|
||||
@@ -29,6 +29,7 @@ import net.momirealms.craftengine.bukkit.plugin.network.handler.*;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.payload.NetWorkDataTypes;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.payload.Payload;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.payload.UnknownPayload;
|
||||
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.MEntityTypes;
|
||||
@@ -105,7 +106,7 @@ public class PacketConsumers {
|
||||
byte yHeadRot = buf.readByte();
|
||||
int data = buf.readVarInt();
|
||||
// Falling blocks
|
||||
int remapped = remap(data);
|
||||
int remapped = user.clientModEnabled() ? remapMOD(data) : remap(data);
|
||||
if (remapped != data) {
|
||||
int xa = buf.readShort();
|
||||
int ya = buf.readShort();
|
||||
@@ -428,7 +429,7 @@ public class PacketConsumers {
|
||||
if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) {
|
||||
return;
|
||||
}
|
||||
int state = remap(before);
|
||||
int state = user.clientModEnabled() ? remapMOD(before) : remap(before);
|
||||
if (state == before) {
|
||||
return;
|
||||
}
|
||||
@@ -450,7 +451,7 @@ public class PacketConsumers {
|
||||
BlockPos blockPos = buf.readBlockPos();
|
||||
int state = buf.readInt();
|
||||
boolean global = buf.readBoolean();
|
||||
int newState = remap(state);
|
||||
int newState = user.clientModEnabled() ? remapMOD(state) : remap(state);
|
||||
if (newState == state) {
|
||||
return;
|
||||
}
|
||||
@@ -1006,7 +1007,7 @@ public class PacketConsumers {
|
||||
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
|
||||
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
|
||||
int id = BlockStateUtils.blockStateToId(blockState);
|
||||
int remapped = remap(id);
|
||||
int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id);
|
||||
if (remapped == id) return;
|
||||
Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option);
|
||||
Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped));
|
||||
@@ -1046,7 +1047,7 @@ public class PacketConsumers {
|
||||
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
|
||||
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
|
||||
int id = BlockStateUtils.blockStateToId(blockState);
|
||||
int remapped = remap(id);
|
||||
int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id);
|
||||
if (remapped == id) return;
|
||||
Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option);
|
||||
Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped));
|
||||
@@ -1086,7 +1087,7 @@ public class PacketConsumers {
|
||||
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
|
||||
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
|
||||
int id = BlockStateUtils.blockStateToId(blockState);
|
||||
int remapped = remap(id);
|
||||
int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id);
|
||||
if (remapped == id) return;
|
||||
Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option);
|
||||
Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped));
|
||||
@@ -1601,6 +1602,10 @@ public class PacketConsumers {
|
||||
}
|
||||
|
||||
mainThreadTask = () -> {
|
||||
if (!furniture.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint);
|
||||
if (EventUtils.fireAndCheckCancel(interactEvent)) {
|
||||
return;
|
||||
@@ -1622,7 +1627,7 @@ public class PacketConsumers {
|
||||
}
|
||||
|
||||
// 必须从网络包层面处理,否则无法获取交互的具体实体
|
||||
if (serverPlayer.isSecondaryUseActive() && itemInHand != null) {
|
||||
if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty()) {
|
||||
// try placing another furniture above it
|
||||
AABB hitBox = furniture.aabbByEntityId(entityId);
|
||||
if (hitBox == null) return;
|
||||
@@ -1884,34 +1889,38 @@ public class PacketConsumers {
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CUSTOM_PAYLOAD = (user, event, packet) -> {
|
||||
try {
|
||||
if (!VersionHelper.isOrAbove1_20_5()) return;
|
||||
if (!VersionHelper.isOrAbove1_20_2()) return;
|
||||
Object payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet);
|
||||
Payload clientPayload;
|
||||
if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) {
|
||||
Payload discardedPayload = DiscardedPayload.from(payload);
|
||||
if (discardedPayload == null || !discardedPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY))
|
||||
clientPayload = DiscardedPayload.from(payload);
|
||||
} else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$UnknownPayload.isInstance(payload)) {
|
||||
clientPayload = UnknownPayload.from(payload);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY))
|
||||
return;
|
||||
FriendlyByteBuf buf = clientPayload.toBuffer();
|
||||
NetWorkDataTypes dataType = buf.readEnumConstant(NetWorkDataTypes.class);
|
||||
if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) {
|
||||
int clientBlockRegistrySize = dataType.decode(buf);
|
||||
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
|
||||
if (clientBlockRegistrySize != serverBlockRegistrySize) {
|
||||
user.kick(Component.translatable(
|
||||
"disconnect.craftengine.block_registry_mismatch",
|
||||
TranslationArgument.numeric(clientBlockRegistrySize),
|
||||
TranslationArgument.numeric(serverBlockRegistrySize)
|
||||
));
|
||||
return;
|
||||
FriendlyByteBuf buf = discardedPayload.toBuffer();
|
||||
NetWorkDataTypes<?> dataType = NetWorkDataTypes.readType(buf);
|
||||
if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) {
|
||||
int clientBlockRegistrySize = dataType.as(Integer.class).decode(buf);
|
||||
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
|
||||
if (clientBlockRegistrySize != serverBlockRegistrySize) {
|
||||
user.kick(Component.translatable(
|
||||
"disconnect.craftengine.block_registry_mismatch",
|
||||
TranslationArgument.numeric(clientBlockRegistrySize),
|
||||
TranslationArgument.numeric(serverBlockRegistrySize)
|
||||
));
|
||||
return;
|
||||
}
|
||||
user.setClientModState(true);
|
||||
} else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) {
|
||||
if (!VersionHelper.isOrAbove1_20_2()) return;
|
||||
if (dataType.as(Boolean.class).decode(buf)) {
|
||||
FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer());
|
||||
dataType.writeType(bufPayload);
|
||||
dataType.as(Boolean.class).encode(bufPayload, true);
|
||||
user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array());
|
||||
}
|
||||
}
|
||||
user.setClientModState(true);
|
||||
} else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) {
|
||||
if (dataType.decode(buf)) {
|
||||
FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer());
|
||||
bufPayload.writeEnumConstant(dataType);
|
||||
dataType.encode(bufPayload, true);
|
||||
user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array());
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
@@ -2124,23 +2133,6 @@ public class PacketConsumers {
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
|
||||
ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
|
||||
if (VersionHelper.isOrAbove1_21_5()) {
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(itemStack);
|
||||
if (!wrapped.isEmpty() && wrapped.isCustomItem()) {
|
||||
Object containerMenu = FastNMS.INSTANCE.field$Player$containerMenu(serverPlayer.serverPlayer());
|
||||
if (containerMenu != null) {
|
||||
ItemStack carried = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.method$AbstractContainerMenu$getCarried(containerMenu));
|
||||
if (ItemStackUtils.isEmpty(carried)) {
|
||||
event.setChanged(true);
|
||||
buf.clear();
|
||||
buf.writeVarInt(event.packetID());
|
||||
Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
|
||||
FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, carried);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> {
|
||||
event.setChanged(true);
|
||||
buf.clear();
|
||||
@@ -2216,11 +2208,18 @@ public class PacketConsumers {
|
||||
|
||||
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> SET_CREATIVE_MODE_SLOT = (user, event) -> {
|
||||
try {
|
||||
if (!(user instanceof BukkitServerPlayer serverPlayer)) return;
|
||||
if (!serverPlayer.isCreativeMode()) return;
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
|
||||
short slotNum = buf.readShort();
|
||||
ItemStack itemStack = VersionHelper.isOrAbove1_20_5() ?
|
||||
FastNMS.INSTANCE.method$FriendlyByteBuf$readUntrustedItem(friendlyBuf) : FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
|
||||
ItemStack itemStack;
|
||||
try {
|
||||
itemStack = VersionHelper.isOrAbove1_20_5() ?
|
||||
FastNMS.INSTANCE.method$FriendlyByteBuf$readUntrustedItem(friendlyBuf) : FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
BukkitItemManager.instance().c2s(itemStack).ifPresent((newItemStack) -> {
|
||||
event.setChanged(true);
|
||||
buf.clear();
|
||||
@@ -2238,9 +2237,70 @@ public class PacketConsumers {
|
||||
}
|
||||
};
|
||||
|
||||
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> CONTAINER_CLICK_1_21_5 = (user, event) -> {
|
||||
try {
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
boolean changed = false;
|
||||
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
|
||||
Object inventory = FastNMS.INSTANCE.method$Player$getInventory(user.serverPlayer());
|
||||
int containerId = buf.readContainerId();
|
||||
int stateId = buf.readVarInt();
|
||||
short slotNum = buf.readShort();
|
||||
byte buttonNum = buf.readByte();
|
||||
int clickType = buf.readVarInt();
|
||||
int i = buf.readVarInt();
|
||||
Int2ObjectMap<Object> changedSlots = new Int2ObjectOpenHashMap<>(i);
|
||||
for (int j = 0; j < i; ++j) {
|
||||
int k = buf.readShort();
|
||||
Object hashedStack = FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$HashedStack$STREAM_CODEC, friendlyBuf);
|
||||
Object serverSideItemStack = FastNMS.INSTANCE.method$Container$getItem(inventory, k);
|
||||
Optional<ItemStack> optional = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(serverSideItemStack).clone(), ((net.momirealms.craftengine.core.entity.player.Player) user));
|
||||
if (optional.isPresent()) {
|
||||
Object clientSideItemStack = FastNMS.INSTANCE.field$CraftItemStack$handle(optional.get());
|
||||
boolean isSync = FastNMS.INSTANCE.method$HashedStack$matches(hashedStack, clientSideItemStack, BukkitItemManager.instance().decoratedHashOpsGenerator());
|
||||
if (isSync) {
|
||||
changed = true;
|
||||
hashedStack = FastNMS.INSTANCE.method$HashedStack$create(serverSideItemStack, BukkitItemManager.instance().decoratedHashOpsGenerator());
|
||||
}
|
||||
}
|
||||
changedSlots.put(k, hashedStack);
|
||||
}
|
||||
Object carriedHashedStack = FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$HashedStack$STREAM_CODEC, friendlyBuf);
|
||||
Object containerMenu = FastNMS.INSTANCE.field$Player$containerMenu(user.serverPlayer());
|
||||
Object serverSideCarriedItemStack = FastNMS.INSTANCE.method$AbstractContainerMenu$getCarried(containerMenu);
|
||||
Optional<ItemStack> optional = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(serverSideCarriedItemStack).clone(), ((net.momirealms.craftengine.core.entity.player.Player) user));
|
||||
if (optional.isPresent()) {
|
||||
Object clientSideCarriedItemStack = FastNMS.INSTANCE.field$CraftItemStack$handle(optional.get());
|
||||
boolean isSync = FastNMS.INSTANCE.method$HashedStack$matches(carriedHashedStack, clientSideCarriedItemStack, BukkitItemManager.instance().decoratedHashOpsGenerator());
|
||||
if (isSync) {
|
||||
changed = true;
|
||||
carriedHashedStack = FastNMS.INSTANCE.method$HashedStack$create(serverSideCarriedItemStack, BukkitItemManager.instance().decoratedHashOpsGenerator());
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
event.setChanged(true);
|
||||
buf.clear();
|
||||
buf.writeVarInt(event.packetID());
|
||||
buf.writeContainerId(containerId);
|
||||
buf.writeVarInt(stateId);
|
||||
buf.writeShort(slotNum);
|
||||
buf.writeByte(buttonNum);
|
||||
buf.writeVarInt(clickType);
|
||||
buf.writeVarInt(changedSlots.size());
|
||||
Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
|
||||
changedSlots.forEach((k, v) -> {
|
||||
buf.writeShort(k);
|
||||
FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$HashedStack$STREAM_CODEC, newFriendlyBuf, v);
|
||||
});
|
||||
FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$HashedStack$STREAM_CODEC, newFriendlyBuf, carriedHashedStack);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ServerboundContainerClickPacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> CONTAINER_CLICK_1_20 = (user, event) -> {
|
||||
try {
|
||||
if (VersionHelper.isOrAbove1_21_5()) return; // 1.21.5+需要其他办法解决同步问题
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
boolean changed = false;
|
||||
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
|
||||
|
||||
@@ -1,64 +1,28 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.network.payload;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
|
||||
public enum NetWorkDataTypes {
|
||||
CLIENT_CUSTOM_BLOCK(NetWorkCodecs.INTEGER),
|
||||
CANCEL_BLOCK_UPDATE(NetWorkCodecs.BOOLEAN);
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
private final NetWorkCodec<?> codec;
|
||||
|
||||
public class NetWorkDataTypes<T> {
|
||||
private static final Map<Integer, NetWorkDataTypes<?>> id2NetWorkDataTypes = new HashMap<>();
|
||||
|
||||
public static final NetWorkDataTypes<Integer> CLIENT_CUSTOM_BLOCK =
|
||||
new NetWorkDataTypes<>(0, FriendlyByteBuf::readInt, FriendlyByteBuf::writeInt);
|
||||
|
||||
public static final NetWorkDataTypes<Boolean> CANCEL_BLOCK_UPDATE =
|
||||
new NetWorkDataTypes<>(1, FriendlyByteBuf::readBoolean, FriendlyByteBuf::writeBoolean);
|
||||
|
||||
static {
|
||||
register(CLIENT_CUSTOM_BLOCK);
|
||||
register(CANCEL_BLOCK_UPDATE);
|
||||
NetWorkDataTypes(NetWorkCodec<?> codec) {
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
private static void register(NetWorkDataTypes<?> type) {
|
||||
id2NetWorkDataTypes.put(type.id, type);
|
||||
public NetWorkCodec<?> codec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
private final int id;
|
||||
private final Function<FriendlyByteBuf, T> decoder;
|
||||
private final BiConsumer<FriendlyByteBuf, T> encoder;
|
||||
|
||||
public NetWorkDataTypes(int id, Function<FriendlyByteBuf, T> decoder, BiConsumer<FriendlyByteBuf, T> encoder) {
|
||||
this.id = id;
|
||||
this.decoder = decoder;
|
||||
this.encoder = encoder;
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> V decode(ByteBuf buf) {
|
||||
return (V) codec.decode(buf);
|
||||
}
|
||||
|
||||
public T decode(FriendlyByteBuf buf) {
|
||||
return decoder.apply(buf);
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf, T data) {
|
||||
encoder.accept(buf, data);
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void writeType(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(id);
|
||||
}
|
||||
|
||||
public static NetWorkDataTypes<?> readType(FriendlyByteBuf buf) {
|
||||
int id = buf.readVarInt();
|
||||
return id2NetWorkDataTypes.get(id);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "unused"})
|
||||
public <R> NetWorkDataTypes<R> as(Class<R> clazz) {
|
||||
return (NetWorkDataTypes<R>) this;
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> void encode(ByteBuf buf, V value) {
|
||||
((NetWorkCodec<V>) codec).encode(buf, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.network.payload;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public record UnknownPayload(Key channel, ByteBuf rawPayload) implements Payload{
|
||||
|
||||
public static UnknownPayload from(Object payload) {
|
||||
try {
|
||||
Object id = NetworkReflections.field$UnknownPayload$id.get(payload);
|
||||
ByteBuf data = (ByteBuf) NetworkReflections.field$UnknownPayload$data.get(payload);
|
||||
Key channel = KeyUtils.resourceLocationToKey(id);
|
||||
return new UnknownPayload(channel, data);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to create UnknownPayload", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FriendlyByteBuf toBuffer() {
|
||||
return new FriendlyByteBuf(rawPayload);
|
||||
}
|
||||
}
|
||||
@@ -2849,10 +2849,6 @@ public final class CoreReflections {
|
||||
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("server.MinecraftServer"))
|
||||
);
|
||||
|
||||
public static final Method method$MinecraftServer$getServer = requireNonNull(
|
||||
ReflectionUtils.getMethod(clazz$MinecraftServer, new String[] { "getServer" })
|
||||
);
|
||||
|
||||
public static final Field field$MinecraftServer$registries = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(clazz$MinecraftServer, clazz$LayeredRegistryAccess, 0)
|
||||
);
|
||||
@@ -3746,7 +3742,7 @@ public final class CoreReflections {
|
||||
.orElse( null);
|
||||
|
||||
public static final Field field$ServerCommonPacketListenerImpl$closed = MiscUtils.requireNonNullIf(
|
||||
ReflectionUtils.getDeclaredField(clazz$ServerCommonPacketListenerImpl, boolean.class, VersionHelper.isOrAbove1_21_6() ? 1 : 2),
|
||||
ReflectionUtils.getDeclaredField(clazz$ServerCommonPacketListenerImpl, "closed", "n"),
|
||||
VersionHelper.isOrAbove1_20_5()
|
||||
);
|
||||
|
||||
@@ -3759,11 +3755,14 @@ public final class CoreReflections {
|
||||
methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask =
|
||||
ReflectionUtils.unreflectMethod(method$ServerConfigurationPacketListenerImpl$finishCurrentTask)
|
||||
.asType(MethodType.methodType(void.class, Object.class, Object.class));
|
||||
} else {
|
||||
methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask = null;
|
||||
}
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
methodHandle$ServerCommonPacketListenerImpl$closedSetter =
|
||||
ReflectionUtils.unreflectSetter(field$ServerCommonPacketListenerImpl$closed)
|
||||
.asType(MethodType.methodType(void.class, Object.class, boolean.class));
|
||||
} else {
|
||||
methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask = null;
|
||||
methodHandle$ServerCommonPacketListenerImpl$closedSetter = null;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -3830,4 +3829,140 @@ public final class CoreReflections {
|
||||
),
|
||||
!VersionHelper.isOrAbove1_21_5()
|
||||
);
|
||||
public static final Object instance$CollisionContext$empty;
|
||||
|
||||
static {
|
||||
try {
|
||||
instance$CollisionContext$empty = requireNonNull(method$CollisionContext$empty.invoke(null));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 1.21.6+
|
||||
public static final Method method$CollisionContext$placementContext = MiscUtils.requireNonNullIf(ReflectionUtils.getStaticMethod(
|
||||
clazz$CollisionContext, clazz$CollisionContext, clazz$Player
|
||||
), VersionHelper.isOrAbove1_21_6());
|
||||
|
||||
public static final Constructor<?> constructor$BlockHitResult = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
CoreReflections.clazz$BlockHitResult, CoreReflections.clazz$Vec3, CoreReflections.clazz$Direction, CoreReflections.clazz$BlockPos, boolean.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$HashOps = MiscUtils.requireNonNullIf(
|
||||
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("util.HashOps")),
|
||||
VersionHelper.isOrAbove1_21_5()
|
||||
);
|
||||
|
||||
public static final Field field$HashOps$CRC32C_INSTANCE = Optional.ofNullable(clazz$HashOps)
|
||||
.map(it -> ReflectionUtils.getDeclaredField(it, it, 0))
|
||||
.orElse(null);
|
||||
|
||||
public static final Object instance$HashOps$CRC32C_INSTANCE;
|
||||
|
||||
static {
|
||||
try {
|
||||
if (VersionHelper.isOrAbove1_21_5()) {
|
||||
instance$HashOps$CRC32C_INSTANCE = field$HashOps$CRC32C_INSTANCE.get(null);
|
||||
} else {
|
||||
instance$HashOps$CRC32C_INSTANCE = null;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new ReflectionInitException("Failed to initialize HashOps", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Class<?> clazz$SnowLayerBlock = requireNonNull(
|
||||
BukkitReflectionUtils.findReobfOrMojmapClass(
|
||||
"world.level.block.BlockSnow",
|
||||
"world.level.block.SnowLayerBlock"
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$SnowLayerBlock$LAYERS = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$SnowLayerBlock, clazz$IntegerProperty, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Object instance$SnowLayerBlock$LAYERS;
|
||||
|
||||
static {
|
||||
try {
|
||||
instance$SnowLayerBlock$LAYERS = field$SnowLayerBlock$LAYERS.get(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ReflectionInitException("Failed to initialize SnowLayerBlock$LAYERS", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Class<?> clazz$DyeItem = requireNonNull(
|
||||
BukkitReflectionUtils.findReobfOrMojmapClass(
|
||||
"world.item.ItemDye",
|
||||
"world.item.DyeItem"
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$Recipe$matches = requireNonNull(
|
||||
VersionHelper.isOrAbove1_21() ?
|
||||
ReflectionUtils.getMethod(clazz$Recipe, boolean.class, clazz$RecipeInput, clazz$Level) :
|
||||
ReflectionUtils.getMethod(clazz$Recipe, boolean.class, clazz$Container, clazz$Level)
|
||||
);
|
||||
|
||||
public static final Method method$Recipe$assemble = requireNonNull(
|
||||
VersionHelper.isOrAbove1_21() ?
|
||||
ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$RecipeInput, clazz$HolderLookup$Provider) :
|
||||
VersionHelper.isOrAbove1_20_5() ?
|
||||
ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$Container, clazz$HolderLookup$Provider) :
|
||||
ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$Container, clazz$RegistryAccess)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$CraftingBookCategory = requireNonNull(
|
||||
BukkitReflectionUtils.findReobfOrMojmapClass(
|
||||
"world.item.crafting.CraftingBookCategory",
|
||||
"world.item.crafting.CraftingBookCategory"
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$CraftingBookCategory$values = requireNonNull(
|
||||
ReflectionUtils.getStaticMethod(clazz$CraftingBookCategory, clazz$CraftingBookCategory.arrayType())
|
||||
);
|
||||
|
||||
public static final Object instance$CraftingBookCategory$BUILDING;
|
||||
public static final Object instance$CraftingBookCategory$REDSTONE;
|
||||
public static final Object instance$CraftingBookCategory$EQUIPMENT;
|
||||
public static final Object instance$CraftingBookCategory$MISC;
|
||||
|
||||
static {
|
||||
try {
|
||||
Object[] values = (Object[]) method$CraftingBookCategory$values.invoke(null);
|
||||
instance$CraftingBookCategory$BUILDING = values[0];
|
||||
instance$CraftingBookCategory$REDSTONE = values[1];
|
||||
instance$CraftingBookCategory$EQUIPMENT = values[2];
|
||||
instance$CraftingBookCategory$MISC = values[3];
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new ReflectionInitException("Failed to initialize CraftingBookCategory", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Class<?> clazz$CraftingInput = MiscUtils.requireNonNullIf(
|
||||
BukkitReflectionUtils.findReobfOrMojmapClass(
|
||||
"world.item.crafting.CraftingInput",
|
||||
"world.item.crafting.CraftingInput"
|
||||
), VersionHelper.isOrAbove1_21()
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$CraftingContainer = requireNonNull(
|
||||
BukkitReflectionUtils.findReobfOrMojmapClass(
|
||||
"world.inventory.InventoryCrafting",
|
||||
"world.inventory.CraftingContainer"
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$DyeableLeatherItem = MiscUtils.requireNonNullIf(
|
||||
BukkitReflectionUtils.findReobfOrMojmapClass(
|
||||
"world.item.IDyeable",
|
||||
"world.item.DyeableLeatherItem"
|
||||
), !VersionHelper.isOrAbove1_20_5()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ public final class MBlocks {
|
||||
public static final Object SHORT_GRASS$defaultState;
|
||||
public static final Object SHULKER_BOX;
|
||||
public static final Object COMPOSTER;
|
||||
public static final Object SNOW;
|
||||
public static final Object WATER;
|
||||
public static final Object WATER$defaultState;
|
||||
|
||||
private static Object getById(String id) {
|
||||
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
|
||||
@@ -35,5 +38,8 @@ public final class MBlocks {
|
||||
SHORT_GRASS$defaultState = FastNMS.INSTANCE.method$Block$defaultState(SHORT_GRASS);
|
||||
SHULKER_BOX = getById("shulker_box");
|
||||
COMPOSTER = getById("composter");
|
||||
SNOW = getById("snow");
|
||||
WATER = getById("water");
|
||||
WATER$defaultState = FastNMS.INSTANCE.method$Block$defaultState(WATER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ public final class MFluids {
|
||||
private MFluids() {}
|
||||
|
||||
public static final Object WATER;
|
||||
public static final Object WATER$defaultState;
|
||||
public static final Object FLOWING_WATER;
|
||||
public static final Object LAVA;
|
||||
public static final Object FLOWING_LAVA;
|
||||
@@ -21,6 +22,7 @@ public final class MFluids {
|
||||
static {
|
||||
try {
|
||||
WATER = getById("water");
|
||||
WATER$defaultState = CoreReflections.method$Fluid$defaultFluidState.invoke(WATER);
|
||||
FLOWING_WATER = getById("flowing_water");
|
||||
LAVA = getById("lava");
|
||||
FLOWING_LAVA = getById("flowing_lava");
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class MItems {
|
||||
private MItems() {}
|
||||
@@ -9,11 +12,17 @@ public final class MItems {
|
||||
public static final Object WATER_BUCKET;
|
||||
public static final Object BARRIER;
|
||||
|
||||
@Nullable
|
||||
private static Object getById(String id) {
|
||||
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
|
||||
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, rl);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object getById(Key id) {
|
||||
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(id));
|
||||
}
|
||||
|
||||
static {
|
||||
AIR = getById("air");
|
||||
WATER_BUCKET = getById("water_bucket");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
@@ -12,6 +13,7 @@ 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;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@@ -21,6 +23,7 @@ public final class MRegistryOps {
|
||||
public static final DynamicOps<Tag> SPARROW_NBT;
|
||||
public static final DynamicOps<Object> JAVA;
|
||||
public static final DynamicOps<JsonElement> JSON;
|
||||
public static final @Nullable DynamicOps<HashCode> HASHCODE; // 1.21.5+
|
||||
|
||||
// 1.20.5+
|
||||
public static final Class<?> clazz$JavaOps = ReflectionUtils.getClazz("com.mojang.serialization.JavaOps");
|
||||
@@ -47,6 +50,7 @@ public final class MRegistryOps {
|
||||
NBT = (DynamicOps<Object>) CoreReflections.method$RegistryOps$create.invoke(null, ReflectionUtils.getDeclaredField(clazz$NbtOps, clazz$NbtOps, 0).get(null), FastNMS.INSTANCE.registryAccess());
|
||||
JSON = (DynamicOps<JsonElement>) CoreReflections.method$RegistryOps$create.invoke(null, JsonOps.INSTANCE, FastNMS.INSTANCE.registryAccess());
|
||||
SPARROW_NBT = (DynamicOps<Tag>) CoreReflections.method$RegistryOps$create.invoke(null, VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE, FastNMS.INSTANCE.registryAccess());
|
||||
HASHCODE = VersionHelper.isOrAbove1_21_5() ? (DynamicOps<HashCode>) CoreReflections.method$RegistryOps$create.invoke(null, CoreReflections.instance$HashOps$CRC32C_INSTANCE, FastNMS.INSTANCE.registryAccess()) : null;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new ReflectionInitException("Failed to init DynamicOps", e);
|
||||
}
|
||||
|
||||
@@ -1248,7 +1248,9 @@ public final class NetworkReflections {
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundCustomPayloadPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, 0)
|
||||
VersionHelper.isOrAbove1_20_2()
|
||||
? ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, clazz$CustomPacketPayload)
|
||||
: ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$FriendlyByteBuf)
|
||||
);
|
||||
|
||||
// 1.20.2+
|
||||
@@ -1594,4 +1596,54 @@ public final class NetworkReflections {
|
||||
List.of("network.protocol.common.ClientboundUpdateTagsPacket", "network.protocol.game.ClientboundUpdateTagsPacket")
|
||||
)
|
||||
);
|
||||
|
||||
// 1.21.5+
|
||||
public static final Class<?> clazz$HashedStack = MiscUtils.requireNonNullIf(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.HashedStack")
|
||||
),
|
||||
VersionHelper.isOrAbove1_21_5()
|
||||
);
|
||||
|
||||
// 1.21.5+
|
||||
public static final Field field$HashedStack$STREAM_CODEC = Optional.ofNullable(clazz$HashedStack)
|
||||
.map(it -> ReflectionUtils.getDeclaredField(it, clazz$StreamCodec, 0))
|
||||
.orElse(null);
|
||||
|
||||
public static final Object instance$HashedStack$STREAM_CODEC;
|
||||
|
||||
static {
|
||||
try {
|
||||
if (VersionHelper.isOrAbove1_21_5()) {
|
||||
instance$HashedStack$STREAM_CODEC = field$HashedStack$STREAM_CODEC.get(null);
|
||||
} else {
|
||||
instance$HashedStack$STREAM_CODEC = null;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new ReflectionInitException("Failed to initialize HashedStack$STREAM_CODEC", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 1.20.2~1.20.4
|
||||
public static final Class<?> clazz$UnknownPayload = MiscUtils.requireNonNullIf(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket$UnknownPayload")
|
||||
),
|
||||
VersionHelper.isOrAbove1_20_2() && !VersionHelper.isOrAbove1_20_5()
|
||||
);
|
||||
|
||||
// 1.20.2~1.20.4
|
||||
public static final Field field$UnknownPayload$id = Optional.ofNullable(clazz$UnknownPayload)
|
||||
.map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$ResourceLocation, 0))
|
||||
.orElse(null);
|
||||
|
||||
// 1.20.2~1.20.4
|
||||
public static final Field field$UnknownPayload$data = Optional.ofNullable(clazz$UnknownPayload)
|
||||
.map(it -> ReflectionUtils.getDeclaredField(it, ByteBuf.class, 0))
|
||||
.orElse(null);
|
||||
|
||||
// 1.20.2~1.20.4
|
||||
public static final Constructor<?> constructor$UnknownPayload = Optional.ofNullable(clazz$UnknownPayload)
|
||||
.map(ReflectionUtils::getTheOnlyConstructor)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@ public class BukkitServerPlayer extends Player {
|
||||
private Key clientSideDimension;
|
||||
// check main hand/offhand interaction
|
||||
private int lastSuccessfulInteraction;
|
||||
// to prevent duplicated events
|
||||
private int lastInteractEntityWithMainHand;
|
||||
private int lastInteractEntityWithOffHand;
|
||||
// re-sync attribute timely to prevent some bugs
|
||||
private long lastAttributeSyncTime;
|
||||
// for breaking blocks
|
||||
@@ -90,8 +93,6 @@ public class BukkitServerPlayer extends Player {
|
||||
// for client visual sync
|
||||
private int resentSoundTick;
|
||||
private int resentSwingTick;
|
||||
// cache used recipe
|
||||
private Key lastUsedRecipe = null;
|
||||
// has fabric client mod or not
|
||||
private boolean hasClientMod = false;
|
||||
// cache if player can break blocks
|
||||
@@ -234,6 +235,24 @@ public class BukkitServerPlayer extends Player {
|
||||
return this.lastSuccessfulInteraction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLastInteractEntityTick(@NotNull InteractionHand hand) {
|
||||
if (hand == InteractionHand.MAIN_HAND) {
|
||||
this.lastInteractEntityWithMainHand = gameTicks();
|
||||
} else {
|
||||
this.lastInteractEntityWithOffHand = gameTicks();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lastInteractEntityCheck(@NotNull InteractionHand hand) {
|
||||
if (hand == InteractionHand.MAIN_HAND) {
|
||||
return gameTicks() == this.lastInteractEntityWithMainHand;
|
||||
} else {
|
||||
return gameTicks() == this.lastInteractEntityWithOffHand;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int gameTicks() {
|
||||
return this.gameTicks;
|
||||
@@ -302,17 +321,29 @@ public class BukkitServerPlayer extends Player {
|
||||
this.plugin.networkManager().sendPacket(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Object packet, boolean immediately, Runnable sendListener) {
|
||||
this.plugin.networkManager().sendPacket(this, packet, immediately, sendListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCustomPayload(Key channel, byte[] data) {
|
||||
try {
|
||||
Object channelKey = KeyUtils.toResourceLocation(channel);
|
||||
Object dataPayload;
|
||||
if (DiscardedPayload.useNewMethod) {
|
||||
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data);
|
||||
Object responsePacket;
|
||||
if (VersionHelper.isOrAbove1_20_2()) {
|
||||
Object dataPayload;
|
||||
if (NetworkReflections.clazz$UnknownPayload != null) {
|
||||
dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
|
||||
} else if (DiscardedPayload.useNewMethod) {
|
||||
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data);
|
||||
} else {
|
||||
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
|
||||
}
|
||||
responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload);
|
||||
} else {
|
||||
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
|
||||
responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(channelKey, FastNMS.INSTANCE.constructor$FriendlyByteBuf(Unpooled.wrappedBuffer(data)));
|
||||
}
|
||||
Object responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload);
|
||||
this.sendPacket(responsePacket, true);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to send custom payload to " + name(), e);
|
||||
@@ -324,8 +355,10 @@ public class BukkitServerPlayer extends Player {
|
||||
try {
|
||||
Object reason = ComponentUtils.adventureToMinecraft(message);
|
||||
Object kickPacket = NetworkReflections.constructor$ClientboundDisconnectPacket.newInstance(reason);
|
||||
this.sendPacket(kickPacket, true);
|
||||
this.nettyChannel().disconnect();
|
||||
this.sendPacket(kickPacket, false, () -> FastNMS.INSTANCE.method$Connection$disconnect(this.connection(), reason));
|
||||
this.nettyChannel().config().setAutoRead(false);
|
||||
Runnable handleDisconnection = () -> FastNMS.INSTANCE.method$Connection$handleDisconnection(this.connection());
|
||||
FastNMS.INSTANCE.method$BlockableEventLoop$scheduleOnMain(handleDisconnection);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to kick " + name(), e);
|
||||
}
|
||||
@@ -336,6 +369,11 @@ public class BukkitServerPlayer extends Player {
|
||||
this.plugin.networkManager().sendPackets(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener) {
|
||||
this.plugin.networkManager().sendPackets(this, packet, immediately, sendListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simulatePacket(Object packet) {
|
||||
this.plugin.networkManager().simulatePacket(this, packet);
|
||||
@@ -600,7 +638,7 @@ public class BukkitServerPlayer extends Player {
|
||||
if (canInstabuild() && (itemMaterial == Material.DEBUG_STICK
|
||||
|| itemMaterial == Material.TRIDENT
|
||||
|| (VersionHelper.isOrAbove1_20_5() && itemMaterial == MaterialUtils.MACE)
|
||||
|| item.is(ItemTags.SWORDS))) {
|
||||
|| item.hasItemTag(ItemTags.SWORDS))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -691,7 +729,8 @@ public class BukkitServerPlayer extends Player {
|
||||
FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)
|
||||
)
|
||||
),
|
||||
packet
|
||||
packet,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -746,12 +785,12 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public float yRot() {
|
||||
return platformPlayer().getPitch();
|
||||
return platformPlayer().getYaw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float xRot() {
|
||||
return platformPlayer().getYaw();
|
||||
return platformPlayer().getPitch();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -844,14 +883,6 @@ public class BukkitServerPlayer extends Player {
|
||||
return resentSwingTick == gameTicks();
|
||||
}
|
||||
|
||||
public Key lastUsedRecipe() {
|
||||
return lastUsedRecipe;
|
||||
}
|
||||
|
||||
public void setLastUsedRecipe(Key lastUsedRecipe) {
|
||||
this.lastUsedRecipe = lastUsedRecipe;
|
||||
}
|
||||
|
||||
public boolean clientModEnabled() {
|
||||
return this.hasClientMod;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.momirealms.craftengine.core.sound.JukeboxSong;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -26,6 +27,37 @@ public class BukkitSoundManager extends AbstractSoundManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerSounds(Collection<Key> sounds) {
|
||||
if (sounds.isEmpty()) return;
|
||||
Object registry = MBuiltInRegistries.SOUND_EVENT;
|
||||
try {
|
||||
CoreReflections.field$MappedRegistry$frozen.set(registry, false);
|
||||
for (Key soundEventId : sounds) {
|
||||
Object resourceLocation = KeyUtils.toResourceLocation(soundEventId);
|
||||
// 检查之前有没有注册过了
|
||||
Object soundEvent = FastNMS.INSTANCE.method$Registry$getValue(registry, resourceLocation);
|
||||
// 只有没注册才注册,否则会报错
|
||||
if (soundEvent == null) {
|
||||
soundEvent = VersionHelper.isOrAbove1_21_2() ?
|
||||
CoreReflections.constructor$SoundEvent.newInstance(resourceLocation, Optional.of(0)) :
|
||||
CoreReflections.constructor$SoundEvent.newInstance(resourceLocation, 0, false);
|
||||
Object holder = CoreReflections.method$Registry$registerForHolder.invoke(null, registry, resourceLocation, soundEvent);
|
||||
CoreReflections.method$Holder$Reference$bindValue.invoke(holder, soundEvent);
|
||||
CoreReflections.field$Holder$Reference$tags.set(holder, Set.of());
|
||||
int id = FastNMS.INSTANCE.method$Registry$getId(registry, soundEvent);
|
||||
super.customSoundsInRegistry.put(id, soundEventId);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to register jukebox songs.", e);
|
||||
} finally {
|
||||
try {
|
||||
CoreReflections.field$MappedRegistry$frozen.set(registry, true);
|
||||
} catch (ReflectiveOperationException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerSongs(Map<Key, JukeboxSong> songs) {
|
||||
if (songs.isEmpty()) return;
|
||||
@@ -40,13 +72,12 @@ public class BukkitSoundManager extends AbstractSoundManager {
|
||||
Object soundId = KeyUtils.toResourceLocation(jukeboxSong.sound());
|
||||
// 检查之前有没有注册过了
|
||||
Object song = FastNMS.INSTANCE.method$Registry$getValue(registry, resourceLocation);
|
||||
|
||||
Object soundEvent = VersionHelper.isOrAbove1_21_2() ?
|
||||
CoreReflections.constructor$SoundEvent.newInstance(soundId, Optional.of(jukeboxSong.range())) :
|
||||
CoreReflections.constructor$SoundEvent.newInstance(soundId, jukeboxSong.range(), false);
|
||||
Object soundHolder = CoreReflections.method$Holder$direct.invoke(null, soundEvent);
|
||||
// 只有没注册才注册,否则会报错
|
||||
if (song == null) {
|
||||
Object soundEvent = VersionHelper.isOrAbove1_21_2() ?
|
||||
CoreReflections.constructor$SoundEvent.newInstance(soundId, Optional.of(jukeboxSong.range())) :
|
||||
CoreReflections.constructor$SoundEvent.newInstance(soundId, jukeboxSong.range(), false);
|
||||
Object soundHolder = CoreReflections.method$Holder$direct.invoke(null, soundEvent);
|
||||
song = CoreReflections.constructor$JukeboxSong.newInstance(soundHolder, ComponentUtils.adventureToMinecraft(jukeboxSong.description()), jukeboxSong.lengthInSeconds(), jukeboxSong.comparatorOutput());
|
||||
Object holder = CoreReflections.method$Registry$registerForHolder.invoke(null, registry, resourceLocation, song);
|
||||
CoreReflections.method$Holder$Reference$bindValue.invoke(holder, song);
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.block.DelegatingBlockState;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -142,4 +143,10 @@ public class BlockStateUtils {
|
||||
Object blockOwner = getBlockOwner(state);
|
||||
return IGNITE_ODDS.getOrDefault(blockOwner, 0) > 0;
|
||||
}
|
||||
|
||||
public static Object getBlockState(Block block) {
|
||||
Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld());
|
||||
Object blockPos = LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ());
|
||||
return FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
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.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import org.bukkit.Location;
|
||||
@@ -15,7 +17,8 @@ import java.util.function.Consumer;
|
||||
|
||||
public class EntityUtils {
|
||||
|
||||
private EntityUtils() {}
|
||||
private EntityUtils() {
|
||||
}
|
||||
|
||||
public static BlockPos getOnPos(Player player) {
|
||||
try {
|
||||
@@ -34,4 +37,11 @@ public class EntityUtils {
|
||||
return LegacyEntityUtils.spawnEntity(world, loc, type, function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Key getEntityType(Entity entity) {
|
||||
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(entity);
|
||||
Object entityType = FastNMS.INSTANCE.method$Entity$getType(nmsEntity);
|
||||
Object id = FastNMS.INSTANCE.method$Registry$getKey(MBuiltInRegistries.ENTITY_TYPE, entityType);
|
||||
return KeyUtils.resourceLocationToKey(id);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import io.papermc.paper.entity.Shearable;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior;
|
||||
import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
|
||||
import net.momirealms.craftengine.core.block.BlockKeys;
|
||||
import net.momirealms.craftengine.core.entity.EntityTypeKeys;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.recipe.RecipeType;
|
||||
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
@@ -12,26 +17,35 @@ import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.QuadFunction;
|
||||
import net.momirealms.craftengine.core.util.TriFunction;
|
||||
import net.momirealms.craftengine.core.world.BlockHitResult;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Levelled;
|
||||
import org.bukkit.block.data.type.Bell;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.block.data.type.ChiseledBookshelf;
|
||||
import org.bukkit.block.data.type.RespawnAnchor;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class InteractUtils {
|
||||
private static final Map<Key, QuadFunction<Player, Item<ItemStack>, BlockData, BlockHitResult, Boolean>> INTERACTIONS = new HashMap<>();
|
||||
private static final Map<Key, QuadFunction<Player, Item<ItemStack>, BlockData, BlockHitResult, Boolean>> WILL_CONSUME = new HashMap<>();
|
||||
private static final Map<Key, TriFunction<Player, Entity, @Nullable Item<ItemStack>, Boolean>> ENTITY_INTERACTIONS = new HashMap<>();
|
||||
private static final Key NOTE_BLOCK_TOP_INSTRUMENTS = Key.of("minecraft:noteblock_top_instruments");
|
||||
|
||||
private InteractUtils() {}
|
||||
|
||||
static {
|
||||
registerInteraction(BlockKeys.NOTE_BLOCK, (player, item, blockState, result) -> result.getDirection() != Direction.UP || !item.is(NOTE_BLOCK_TOP_INSTRUMENTS));
|
||||
registerInteraction(BlockKeys.NOTE_BLOCK, (player, item, blockState, result) -> result.getDirection() != Direction.UP || !item.hasItemTag(NOTE_BLOCK_TOP_INSTRUMENTS));
|
||||
registerInteraction(BlockKeys.CAKE, (player, item, blockState, result) -> !canEat(player, false));
|
||||
registerInteraction(BlockKeys.CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false));
|
||||
registerInteraction(BlockKeys.WHITE_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false));
|
||||
@@ -50,6 +64,43 @@ public class InteractUtils {
|
||||
registerInteraction(BlockKeys.GREEN_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false));
|
||||
registerInteraction(BlockKeys.RED_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false));
|
||||
registerInteraction(BlockKeys.BLACK_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false));
|
||||
registerInteraction(BlockKeys.COMMAND_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE);
|
||||
registerInteraction(BlockKeys.CHAIN_COMMAND_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE);
|
||||
registerInteraction(BlockKeys.REPEATING_COMMAND_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE);
|
||||
registerInteraction(BlockKeys.JIGSAW, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE);
|
||||
registerInteraction(BlockKeys.STRUCTURE_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE);
|
||||
registerInteraction(BlockKeys.TEST_INSTANCE_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE);
|
||||
registerInteraction(BlockKeys.TEST_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE);
|
||||
registerInteraction(BlockKeys.LIGHT, (player, item, blockState, result) -> item.vanillaId().equals(ItemKeys.LIGHT));
|
||||
registerInteraction(BlockKeys.LODESTONE, (player, item, blockState, result) -> item.vanillaId().equals(ItemKeys.COMPASS));
|
||||
registerInteraction(BlockKeys.BEE_NEST, (player, item, blockState, result) -> {
|
||||
Key id = item.vanillaId();
|
||||
return ItemKeys.SHEARS.equals(id) || ItemKeys.GLASS_BOTTLE.equals(id);
|
||||
});
|
||||
registerInteraction(BlockKeys.BEEHIVE, (player, item, blockState, result) -> {
|
||||
Key id = item.vanillaId();
|
||||
return ItemKeys.SHEARS.equals(id) || ItemKeys.GLASS_BOTTLE.equals(id);
|
||||
});
|
||||
registerInteraction(BlockKeys.POWDER_SNOW, (player, item, blockState, result) -> item.vanillaId().equals(ItemKeys.BUCKET));
|
||||
registerInteraction(BlockKeys.REDSTONE_ORE, (player, item, blockState, result) -> {
|
||||
Optional<List<ItemBehavior>> behaviors = item.getItemBehavior();
|
||||
if (behaviors.isPresent()) {
|
||||
for (ItemBehavior behavior : behaviors.get()) {
|
||||
if (behavior instanceof BlockItemBehavior) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
registerInteraction(BlockKeys.DEEPSLATE_REDSTONE_ORE, (player, item, blockState, result) -> {
|
||||
Optional<List<ItemBehavior>> behaviors = item.getItemBehavior();
|
||||
if (behaviors.isPresent()) {
|
||||
for (ItemBehavior behavior : behaviors.get()) {
|
||||
if (behavior instanceof BlockItemBehavior) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
registerInteraction(BlockKeys.BELL, (player, item, blockState, result) -> {
|
||||
Direction direction = result.getDirection();
|
||||
BlockPos pos = result.getBlockPos();
|
||||
@@ -78,17 +129,26 @@ public class InteractUtils {
|
||||
});
|
||||
registerInteraction(BlockKeys.SOUL_CAMPFIRE, (player, item, blockState, result) -> {
|
||||
if (!Config.enableRecipeSystem()) return false;
|
||||
return BukkitRecipeManager.instance().recipeByInput(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new UniqueIdItem<>(
|
||||
item.recipeIngredientId(), item
|
||||
))) != null;
|
||||
return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(UniqueIdItem.of(item))) != null;
|
||||
});
|
||||
registerInteraction(BlockKeys.CAMPFIRE, (player, item, blockState, result) -> {
|
||||
if (!Config.enableRecipeSystem()) return false;
|
||||
return BukkitRecipeManager.instance().recipeByInput(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new UniqueIdItem<>(
|
||||
item.recipeIngredientId(), item
|
||||
))) != null;
|
||||
return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(UniqueIdItem.of(item))) != null;
|
||||
});
|
||||
registerInteraction(BlockKeys.CHISELED_BOOKSHELF, (player, item, blockState, result) -> {
|
||||
if (!(blockState instanceof ChiseledBookshelf chiseledBookshelf)) return false;
|
||||
return DirectionUtils.toDirection(chiseledBookshelf.getFacing()) == result.getDirection();
|
||||
});
|
||||
registerInteraction(BlockKeys.COMPOSTER, (player, item, blockState, result) -> {
|
||||
if (item.getItem().getType().isCompostable()) return true;
|
||||
return blockState instanceof Levelled levelled && levelled.getLevel() == levelled.getMaximumLevel();
|
||||
});
|
||||
registerInteraction(BlockKeys.RESPAWN_ANCHOR, (player, item, blockState, result) -> {
|
||||
if (item.vanillaId().equals(ItemKeys.GLOWSTONE)) return true;
|
||||
return blockState instanceof RespawnAnchor respawnAnchor && respawnAnchor.getCharges() != 0;
|
||||
});
|
||||
registerInteraction(BlockKeys.DECORATED_POT, (player, item, blockState, result) -> true);
|
||||
registerInteraction(BlockKeys.FLOWER_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);
|
||||
@@ -264,14 +324,129 @@ public class InteractUtils {
|
||||
registerInteraction(BlockKeys.RED_BED, (player, item, blockState, result) -> true);
|
||||
registerInteraction(BlockKeys.BLACK_BED, (player, item, blockState, result) -> true);
|
||||
registerInteraction(BlockKeys.DRAGON_EGG, (player, item, blockState, result) -> true);
|
||||
registerInteraction(BlockKeys.REPEATING_COMMAND_BLOCK, (player, item, blockState, result) -> true);
|
||||
registerInteraction(BlockKeys.CHAIN_COMMAND_BLOCK, (player, item, blockState, result) -> true);
|
||||
registerInteraction(BlockKeys.COMMAND_BLOCK, (player, item, blockState, result) -> true);
|
||||
}
|
||||
|
||||
static {
|
||||
registerWillConsume(BlockKeys.CACTUS, (player, item, blockState, result) ->
|
||||
result.getDirection() == Direction.UP && item.id().equals(ItemKeys.CACTUS));
|
||||
registerWillConsume(BlockKeys.CAULDRON, (player, item, blockState, result) -> {
|
||||
Key id = item.vanillaId();
|
||||
return ItemKeys.WATER_BUCKET.equals(id) || ItemKeys.LAVA_BUCKET.equals(id);
|
||||
});
|
||||
registerWillConsume(BlockKeys.LAVA_CAULDRON, (player, item, blockState, result) -> {
|
||||
Key id = item.vanillaId();
|
||||
return ItemKeys.BUCKET.equals(id) || ItemKeys.LAVA_BUCKET.equals(id) || ItemKeys.WATER_BUCKET.equals(id);
|
||||
});
|
||||
registerWillConsume(BlockKeys.WATER_CAULDRON, (player, item, blockState, result) -> {
|
||||
if (blockState instanceof Levelled levelled && levelled.getLevel() == levelled.getMaximumLevel())
|
||||
return item.vanillaId().equals(ItemKeys.BUCKET);
|
||||
Key id = item.vanillaId();
|
||||
return ItemKeys.GLASS_BOTTLE.equals(id) || ItemKeys.WATER_BUCKET.equals(id) || ItemKeys.LAVA_BUCKET.equals(id);
|
||||
});
|
||||
}
|
||||
|
||||
static {
|
||||
registerEntityInteraction(EntityTypeKeys.BEE, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.FOX, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.FROG, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.PANDA, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.HOGLIN, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.OCELOT, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.RABBIT, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.TURTLE, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.CHICKEN, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.SNIFFER, (player, entity, item) -> canFeed(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.AXOLOTL, (player, entity, item) ->
|
||||
canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET)));
|
||||
registerEntityInteraction(EntityTypeKeys.COD, (player, entity, item) ->
|
||||
item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET));
|
||||
registerEntityInteraction(EntityTypeKeys.SALMON, (player, entity, item) ->
|
||||
item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET));
|
||||
registerEntityInteraction(EntityTypeKeys.TROPICAL_FISH, (player, entity, item) ->
|
||||
item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET));
|
||||
registerEntityInteraction(EntityTypeKeys.PUFFERFISH, (player, entity, item) ->
|
||||
item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET));
|
||||
registerEntityInteraction(EntityTypeKeys.TADPOLE, (player, entity, item) ->
|
||||
item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET));
|
||||
registerEntityInteraction(EntityTypeKeys.SNOW_GOLEM, (player, entity, item) ->
|
||||
shearable(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.SHEEP, (player, entity, item) ->
|
||||
canFeed(entity, item) || shearable(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.BOGGED, (player, entity, item) ->
|
||||
canFeed(entity, item) || shearable(entity, item));
|
||||
registerEntityInteraction(EntityTypeKeys.MOOSHROOM, (player, entity, item) ->
|
||||
canFeed(entity, item) || shearable(entity, item) || (item != null && (item.vanillaId().equals(ItemKeys.BUCKET) || item.vanillaId().equals(ItemKeys.BOWL))));
|
||||
registerEntityInteraction(EntityTypeKeys.COW, (player, entity, item) ->
|
||||
canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.BUCKET)));
|
||||
registerEntityInteraction(EntityTypeKeys.GOAT, (player, entity, item) ->
|
||||
canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.BUCKET)));
|
||||
registerEntityInteraction(EntityTypeKeys.CREEPER, (player, entity, item) ->
|
||||
item != null && item.vanillaId().equals(ItemKeys.FLINT_AND_STEEL));
|
||||
registerEntityInteraction(EntityTypeKeys.PIGLIN, (player, entity, item) ->
|
||||
item != null && item.vanillaId().equals(ItemKeys.GOLD_INGOT));
|
||||
registerEntityInteraction(EntityTypeKeys.ARMADILLO, (player, entity, item) ->
|
||||
canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.BRUSH)));
|
||||
registerEntityInteraction(EntityTypeKeys.ZOMBIE_HORSE, (player, entity, item) ->
|
||||
entity instanceof Tameable tameable && tameable.isTamed());
|
||||
registerEntityInteraction(EntityTypeKeys.SKELETON_HORSE, (player, entity, item) ->
|
||||
entity instanceof Tameable tameable && tameable.isTamed());
|
||||
registerEntityInteraction(EntityTypeKeys.PIG, (player, entity, item) ->
|
||||
canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.SADDLE) && !hasSaddle(player, entity)) || (hasSaddle(player, entity) && !player.isSneaking()));
|
||||
registerEntityInteraction(EntityTypeKeys.STRIDER, (player, entity, item) ->
|
||||
canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.SADDLE) && !hasSaddle(player, entity)) || (hasSaddle(player, entity) && !player.isSneaking()));
|
||||
registerEntityInteraction(EntityTypeKeys.WOLF, (player, entity, item) -> canFeed(entity, item) || isPetOwner(player, entity));
|
||||
registerEntityInteraction(EntityTypeKeys.CAT, (player, entity, item) -> canFeed(entity, item) || isPetOwner(player, entity));
|
||||
registerEntityInteraction(EntityTypeKeys.BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.OAK_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.SPRUCE_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.BIRCH_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.JUNGLE_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.ACACIA_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.DARK_OAK_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.MANGROVE_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.CHERRY_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.PALE_OAK_BOAT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.BAMBOO_RAFT, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.MINECART, (player, entity, item) -> !player.isSneaking());
|
||||
registerEntityInteraction(EntityTypeKeys.PARROT, (player, entity, item) -> {
|
||||
if (item != null && item.hasItemTag(Key.of("parrot_poisonous_food"))) return true;
|
||||
return canFeed(entity, item) || isPetOwner(player, entity);
|
||||
});
|
||||
registerEntityInteraction(EntityTypeKeys.HAPPY_GHAST, (player, entity, item) -> {
|
||||
if (item != null && item.vanillaId().equals(ItemKeys.HARNESS)) return true;
|
||||
if (entity instanceof HappyGhast happyGhast && !player.isSneaking()) {
|
||||
ItemStack bodyItem = happyGhast.getEquipment().getItem(EquipmentSlot.BODY);
|
||||
return BukkitItemManager.instance().wrap(bodyItem).hasItemTag(Key.of("harnesses"));
|
||||
}
|
||||
return canFeed(entity, item);
|
||||
});
|
||||
registerEntityInteraction(EntityTypeKeys.ALLAY, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.HORSE, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.DONKEY, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.MULE, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.VILLAGER, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.WANDERING_TRADER, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.LLAMA, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.TRADER_LLAMA, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.CAMEL, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.ITEM_FRAME, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.GLOW_ITEM_FRAME, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.INTERACTION, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.OAK_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.SPRUCE_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.BIRCH_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.JUNGLE_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.ACACIA_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.DARK_OAK_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.MANGROVE_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.CHERRY_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.PALE_OAK_CHEST_BOAT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.BAMBOO_CHEST_RAFT, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.CHEST_MINECART, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.FURNACE_MINECART, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.HOPPER_MINECART, (player, entity, item) -> true);
|
||||
registerEntityInteraction(EntityTypeKeys.COMMAND_BLOCK_MINECART, (player, entity, item) -> true);
|
||||
}
|
||||
|
||||
private static void registerInteraction(Key key, QuadFunction<org.bukkit.entity.Player, Item<ItemStack>, BlockData, BlockHitResult, Boolean> function) {
|
||||
@@ -288,6 +463,13 @@ public class InteractUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerEntityInteraction(Key key, TriFunction<Player, Entity, @Nullable Item<ItemStack>, Boolean> function) {
|
||||
var previous = ENTITY_INTERACTIONS.put(key, function);
|
||||
if (previous != null) {
|
||||
CraftEngine.instance().logger().warn("Duplicated entity interaction check: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInteractable(Player player, BlockData state, BlockHitResult hit, @Nullable Item<ItemStack> item) {
|
||||
Key blockType = BlockStateUtils.getBlockOwnerIdFromData(state);
|
||||
if (INTERACTIONS.containsKey(blockType)) {
|
||||
@@ -297,6 +479,11 @@ public class InteractUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEntityInteractable(Player player, Entity entity, @Nullable Item<ItemStack> item) {
|
||||
TriFunction<Player, Entity, Item<ItemStack>, Boolean> func = ENTITY_INTERACTIONS.get(EntityUtils.getEntityType(entity));
|
||||
return func != null && func.apply(player, entity, item);
|
||||
}
|
||||
|
||||
public static boolean willConsume(Player player, BlockData state, BlockHitResult hit, @Nullable Item<ItemStack> item) {
|
||||
if (item == null) return false;
|
||||
Key blockType = BlockStateUtils.getBlockOwnerIdFromData(state);
|
||||
@@ -306,8 +493,24 @@ public class InteractUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean canEat(Player player, boolean ignoreHunger) {
|
||||
return ignoreHunger || player.isInvulnerable() || player.getFoodLevel() < 20;
|
||||
}
|
||||
|
||||
private static boolean canFeed(Entity entity, Item<ItemStack> item) {
|
||||
return entity instanceof Animals && item.hasItemTag(Key.of(EntityUtils.getEntityType(entity).value() + "_food"));
|
||||
}
|
||||
|
||||
private static boolean hasSaddle(Player player, Entity entity) {
|
||||
return entity instanceof Steerable steerable && steerable.hasSaddle() && !player.isSneaking();
|
||||
}
|
||||
|
||||
private static boolean shearable(Entity entity, Item<ItemStack> item) {
|
||||
return entity instanceof Shearable shearable && item.vanillaId().equals(ItemKeys.SHEARS) && shearable.readyToBeSheared();
|
||||
}
|
||||
|
||||
private static boolean isPetOwner(Player player, Entity entity) {
|
||||
return entity instanceof Tameable tameable && tameable.isTamed() && player.getUniqueId().equals(tameable.getOwnerUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,14 @@ package net.momirealms.craftengine.bukkit.util;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ItemStackUtils {
|
||||
public final class ItemStackUtils {
|
||||
|
||||
private ItemStackUtils() {}
|
||||
|
||||
@@ -43,4 +46,9 @@ public class ItemStackUtils {
|
||||
return FastNMS.INSTANCE.method$CraftItemStack$asCraftCopy(itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
public static UniqueIdItem<ItemStack> getUniqueIdItem(@Nullable ItemStack itemStack) {
|
||||
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
|
||||
return UniqueIdItem.of(wrappedItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public class ItemTags {
|
||||
|
||||
public static final Key AXES = Key.of("minecraft:axes");
|
||||
public static final Key SWORDS = Key.of("minecraft:swords");
|
||||
public static final Key DYEABLE = Key.of("minecraft:dyeable");
|
||||
|
||||
private ItemTags() {}
|
||||
|
||||
|
||||
@@ -1,50 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RecipeUtils {
|
||||
|
||||
private RecipeUtils() {}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Object> getIngredientsFromShapedRecipe(Object recipe) {
|
||||
List<Object> ingredients = new ArrayList<>();
|
||||
try {
|
||||
if (VersionHelper.isOrAbove1_20_3()) {
|
||||
Object pattern = CoreReflections.field$1_20_3$ShapedRecipe$pattern.get(recipe);
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
List<Optional<Object>> optionals = (List<Optional<Object>>) CoreReflections.field$ShapedRecipePattern$ingredients1_21_2.get(pattern);
|
||||
for (Optional<Object> optional : optionals) {
|
||||
optional.ifPresent(ingredients::add);
|
||||
}
|
||||
} else {
|
||||
List<Object> objectList = (List<Object>) CoreReflections.field$ShapedRecipePattern$ingredients1_20_3.get(pattern);
|
||||
for (Object object : objectList) {
|
||||
Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object);
|
||||
// is empty or not
|
||||
if (values.length != 0) {
|
||||
ingredients.add(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<Object> objectList = (List<Object>) CoreReflections.field$1_20_1$ShapedRecipe$recipeItems.get(recipe);
|
||||
for (Object object : objectList) {
|
||||
Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object);
|
||||
if (values.length != 0) {
|
||||
ingredients.add(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to get ingredients from shaped recipe", e);
|
||||
}
|
||||
return ingredients;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ package net.momirealms.craftengine.bukkit.world;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
@@ -21,11 +24,15 @@ public class BukkitBlockInWorld implements BlockInWorld {
|
||||
|
||||
@Override
|
||||
public boolean canBeReplaced(BlockPlaceContext context) {
|
||||
ImmutableBlockState customState = CraftEngineBlocks.getCustomBlockState(this.block);
|
||||
Object state = BlockStateUtils.getBlockState(this.block);
|
||||
ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
|
||||
if (customState != null && !customState.isEmpty()) {
|
||||
return customState.behavior().canBeReplaced(context, customState);
|
||||
}
|
||||
return this.block.isReplaceable();
|
||||
if (BlockStateUtils.getBlockOwner(state) == MBlocks.SNOW) {
|
||||
return (int) FastNMS.INSTANCE.method$StateHolder$getValue(state, CoreReflections.instance$SnowLayerBlock$LAYERS) == 1;
|
||||
}
|
||||
return BlockStateUtils.isReplaceable(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.world;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
@@ -88,6 +87,11 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CEWorld[] getWorlds() {
|
||||
return this.worldArray;
|
||||
}
|
||||
|
||||
private void resetWorldArray() {
|
||||
this.worldArray = this.worlds.values().toArray(new CEWorld[0]);
|
||||
}
|
||||
@@ -218,7 +222,6 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
this.lastVisitedUUID = null;
|
||||
}
|
||||
this.resetWorldArray();
|
||||
|
||||
} finally {
|
||||
this.worldMapLock.writeLock().unlock();
|
||||
}
|
||||
@@ -328,80 +331,75 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
|
||||
Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunk.getX(), chunk.getZ());
|
||||
Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk);
|
||||
for (int i = 0; i < ceSections.length; i++) {
|
||||
CESection ceSection = ceSections[i];
|
||||
Object section = sections[i];
|
||||
if (Config.syncCustomBlocks()) {
|
||||
Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section);
|
||||
Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer);
|
||||
Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data);
|
||||
boolean requiresSync = false;
|
||||
if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) {
|
||||
Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette);
|
||||
if (BlockStateUtils.isCustomBlock(onlyBlockState)) {
|
||||
synchronized (sections) {
|
||||
for (int i = 0; i < ceSections.length; i++) {
|
||||
CESection ceSection = ceSections[i];
|
||||
Object section = sections[i];
|
||||
if (Config.syncCustomBlocks()) {
|
||||
Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section);
|
||||
Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer);
|
||||
Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data);
|
||||
boolean requiresSync = false;
|
||||
if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) {
|
||||
Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette);
|
||||
if (BlockStateUtils.isCustomBlock(onlyBlockState)) {
|
||||
requiresSync = true;
|
||||
}
|
||||
} else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) {
|
||||
Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette);
|
||||
for (Object blockState : blockStates) {
|
||||
if (blockState != null) {
|
||||
if (BlockStateUtils.isCustomBlock(blockState)) {
|
||||
requiresSync = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) {
|
||||
Object biMap = CoreReflections.field$HashMapPalette$values.get(palette);
|
||||
Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap);
|
||||
for (Object blockState : blockStates) {
|
||||
if (blockState != null) {
|
||||
if (BlockStateUtils.isCustomBlock(blockState)) {
|
||||
requiresSync = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requiresSync = true;
|
||||
}
|
||||
} else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) {
|
||||
Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette);
|
||||
for (Object blockState : blockStates) {
|
||||
if (blockState != null) {
|
||||
if (BlockStateUtils.isCustomBlock(blockState)) {
|
||||
requiresSync = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) {
|
||||
Object biMap = CoreReflections.field$HashMapPalette$values.get(palette);
|
||||
Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap);
|
||||
for (Object blockState : blockStates) {
|
||||
if (blockState != null) {
|
||||
if (BlockStateUtils.isCustomBlock(blockState)) {
|
||||
requiresSync = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requiresSync = true;
|
||||
}
|
||||
if (requiresSync) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z);
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState);
|
||||
if (optionalCustomState.isPresent()) {
|
||||
ceSection.setBlockState(x, y, z, optionalCustomState.get());
|
||||
if (requiresSync) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z);
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState);
|
||||
if (optionalCustomState.isPresent()) {
|
||||
ceSection.setBlockState(x, y, z, optionalCustomState.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Config.restoreCustomBlocks()) {
|
||||
if (!ceSection.statesContainer().isEmpty()) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
ImmutableBlockState customState = ceSection.getBlockState(x, y, z);
|
||||
if (!customState.isEmpty() && customState.customBlockState() != null) {
|
||||
FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false);
|
||||
if (Config.restoreCustomBlocks()) {
|
||||
if (!ceSection.statesContainer().isEmpty()) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
ImmutableBlockState customState = ceSection.getBlockState(x, y, z);
|
||||
if (!customState.isEmpty() && customState.customBlockState() != null) {
|
||||
FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int finalI = i;
|
||||
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
|
||||
(injected) -> sections[finalI] = injected);
|
||||
}
|
||||
if (Config.enableRecipeSystem()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> blockEntities = (Map<Object, Object>) FastNMS.INSTANCE.field$ChunkAccess$blockEntities(levelChunk);
|
||||
for (Object blockEntity : blockEntities.values()) {
|
||||
RecipeInjector.injectCookingBlockEntity(blockEntity);
|
||||
int finalI = i;
|
||||
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
|
||||
(injected) -> sections[finalI] = injected);
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
|
||||
@@ -57,8 +57,12 @@ resource-pack:
|
||||
enable: true
|
||||
# Define the name of the overlay folders
|
||||
overlay-format: "ce_overlay_{version}"
|
||||
# Allowed values:
|
||||
# - 1.20.1, 1.21, 1.21.8, etc.
|
||||
# - LATEST: the latest client version
|
||||
# - SERVER: the current server version
|
||||
supported-version:
|
||||
min: "1.20"
|
||||
min: SERVER
|
||||
max: LATEST
|
||||
# Remove 1.21.5+ tinted_leaves particles
|
||||
remove-tinted-leaves-particle: true
|
||||
@@ -84,8 +88,6 @@ resource-pack:
|
||||
ip: "localhost"
|
||||
port: 8163
|
||||
protocol: "http"
|
||||
# The optional URL must be complete and include a trailing slash / at the end.
|
||||
#url: "http://localhost:8163/"
|
||||
deny-non-minecraft-request: true
|
||||
one-time-token: true
|
||||
rate-limit:
|
||||
@@ -296,6 +298,8 @@ recipe:
|
||||
# Selective recipe disabling (safer alternative to 'all: true')
|
||||
# Example: ["minecraft:wooden_sword", "minecraft:stone_hoe"]
|
||||
list: []
|
||||
# You can use items from other plugins by adding the supported plugin names here
|
||||
ingredient-sources: []
|
||||
|
||||
gui:
|
||||
browser:
|
||||
@@ -352,6 +356,8 @@ gui:
|
||||
title: "<white><shift:-11><image:internal:stonecutting_recipe>"
|
||||
smithing-transform:
|
||||
title: "<white><shift:-11><image:internal:smithing_transform_recipe>"
|
||||
brewing:
|
||||
title: "<white><shift:-11><image:internal:brewing_recipe>"
|
||||
|
||||
performance:
|
||||
# Maximum chain update depth when fixing client visuals
|
||||
@@ -376,12 +382,12 @@ chunk-system:
|
||||
# Settings for injection
|
||||
injection:
|
||||
# Requires a restart to apply
|
||||
# SECTION: Inject the LevelChunkSection (Faster & Experimental) since 0.0.53
|
||||
# SECTION: Inject the LevelChunkSection
|
||||
# PALETTE: Inject the PalettedContainer
|
||||
target: PALETTE
|
||||
target: SECTION
|
||||
# Enables faster injection method
|
||||
# Note: May not work with certain server forks that alter chunk class structure (In most cases it won't conflict)
|
||||
use-fast-method: false
|
||||
use-fast-method: true
|
||||
# Auto-convert custom blocks -> vanilla blocks when unloading chunks
|
||||
#
|
||||
# - When ENABLED (true):
|
||||
|
||||
@@ -57,6 +57,7 @@ categories:
|
||||
- default:bench
|
||||
- default:table_lamp
|
||||
- default:wooden_chair
|
||||
- default:flower_basket
|
||||
default:misc:
|
||||
name: <!i><gray><i18n:category.misc></gray>
|
||||
hidden: true
|
||||
|
||||
@@ -132,4 +132,113 @@ items:
|
||||
loot:
|
||||
template: default:loot_table/furniture
|
||||
arguments:
|
||||
item: default:wooden_chair
|
||||
item: default:wooden_chair
|
||||
|
||||
items#flower_basket:
|
||||
default:flower_basket:
|
||||
material: nether_brick
|
||||
custom-model-data: 2003
|
||||
data:
|
||||
item-name: <!i><i18n:item.flower_basket>
|
||||
model:
|
||||
template: default:model/simplified_generated
|
||||
arguments:
|
||||
path: minecraft:item/custom/flower_basket_2d
|
||||
behavior:
|
||||
type: furniture_item
|
||||
furniture: default:flower_basket
|
||||
default:flower_basket_ground:
|
||||
material: nether_brick
|
||||
custom-model-data: 2004
|
||||
model:
|
||||
type: minecraft:model
|
||||
path: minecraft:item/custom/flower_basket_ground
|
||||
default:flower_basket_wall:
|
||||
material: nether_brick
|
||||
custom-model-data: 2005
|
||||
model:
|
||||
type: minecraft:model
|
||||
path: minecraft:item/custom/flower_basket_wall
|
||||
default:flower_basket_ceiling:
|
||||
material: nether_brick
|
||||
custom-model-data: 2006
|
||||
model:
|
||||
type: minecraft:model
|
||||
path: minecraft:item/custom/flower_basket_ceiling
|
||||
|
||||
furniture#flower_basket:
|
||||
default:flower_basket:
|
||||
settings:
|
||||
item: default:flower_basket
|
||||
sounds:
|
||||
break: minecraft:block.grass.break
|
||||
place: minecraft:block.grass.place
|
||||
loot:
|
||||
template: default:loot_table/furniture
|
||||
arguments:
|
||||
item: default:flower_basket
|
||||
placement:
|
||||
ground:
|
||||
rules:
|
||||
rotation: ANY
|
||||
alignment: ANY
|
||||
elements:
|
||||
- item: default:flower_basket_ground
|
||||
display-transform: NONE
|
||||
billboard: FIXED
|
||||
position: 0,0,0
|
||||
translation: 0,0.5,0
|
||||
hitboxes:
|
||||
- type: interaction
|
||||
can-use-item-on: true
|
||||
can-be-hit-by-projectile: true
|
||||
blocks-building: true
|
||||
position: 0,0,0
|
||||
width: 0.7
|
||||
height: 0.5
|
||||
interactive: true
|
||||
wall:
|
||||
rules:
|
||||
alignment: ANY
|
||||
elements:
|
||||
- item: default:flower_basket_wall
|
||||
display-transform: NONE
|
||||
billboard: FIXED
|
||||
position: 0,0,0.2
|
||||
translation: 0,0,0
|
||||
hitboxes:
|
||||
- type: interaction
|
||||
can-use-item-on: true
|
||||
can-be-hit-by-projectile: true
|
||||
blocks-building: true
|
||||
position: 0.215,-0.3,0.23
|
||||
width: 0.46
|
||||
height: 0.75
|
||||
interactive: true
|
||||
- type: interaction
|
||||
can-use-item-on: true
|
||||
can-be-hit-by-projectile: true
|
||||
blocks-building: true
|
||||
position: -0.215,-0.3,0.23
|
||||
width: 0.46
|
||||
height: 0.75
|
||||
interactive: true
|
||||
ceiling:
|
||||
rules:
|
||||
rotation: ANY
|
||||
alignment: ANY
|
||||
elements:
|
||||
- item: default:flower_basket_ceiling
|
||||
display-transform: NONE
|
||||
billboard: FIXED
|
||||
position: 0,-0.46,0
|
||||
translation: 0,0,0
|
||||
hitboxes:
|
||||
- type: interaction
|
||||
can-use-item-on: true
|
||||
can-be-hit-by-projectile: true
|
||||
blocks-building: true
|
||||
position: 0,-0.7,0
|
||||
width: 0.7
|
||||
height: 0.7
|
||||
interactive: true
|
||||
@@ -44,6 +44,7 @@ i18n:
|
||||
item.flame_elytra: Flame Elytra
|
||||
item.pebble: Pebble
|
||||
item.cap: Cap
|
||||
item.flower_basket: Flower Basket
|
||||
category.default.name: Default Assets
|
||||
category.default.lore: Contains the default configuration of CraftEngine
|
||||
category.palm_tree: Palm Tree
|
||||
@@ -98,6 +99,7 @@ i18n:
|
||||
item.flame_elytra: 烈焰鞘翅
|
||||
item.pebble: 石子
|
||||
item.cap: 鸭舌帽
|
||||
item.flower_basket: 花篮
|
||||
category.default.name: 默认资产
|
||||
category.default.lore: 包含了CraftEngine的默认配置
|
||||
category.palm_tree: 棕榈树
|
||||
|
||||
@@ -326,6 +326,7 @@ equipments#topaz:
|
||||
recipes#topaz:
|
||||
default:topaz_shovel:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- A
|
||||
- B
|
||||
@@ -338,6 +339,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_axe:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- 'AA'
|
||||
- 'AB'
|
||||
@@ -350,6 +352,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_sword:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- A
|
||||
- A
|
||||
@@ -362,6 +365,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_hoe:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- 'AA'
|
||||
- ' B'
|
||||
@@ -374,6 +378,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_pickaxe:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- 'AAA'
|
||||
- ' B '
|
||||
@@ -386,6 +391,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_helmet:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- AAA
|
||||
- A A
|
||||
@@ -396,6 +402,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_chestplate:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- A A
|
||||
- AAA
|
||||
@@ -407,6 +414,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_leggings:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- AAA
|
||||
- A A
|
||||
@@ -418,6 +426,7 @@ recipes#topaz:
|
||||
count: 1
|
||||
default:topaz_boots:
|
||||
type: shaped
|
||||
category: equipment
|
||||
pattern:
|
||||
- A A
|
||||
- A A
|
||||
|
||||
@@ -348,7 +348,7 @@ items:
|
||||
arguments:
|
||||
path: minecraft:item/custom/palm_door
|
||||
behavior:
|
||||
type: block_item
|
||||
type: double_high_block_item
|
||||
block:
|
||||
behavior:
|
||||
type: door_block
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"textures": {
|
||||
"1": "item/custom/flower_basket",
|
||||
"particle": "item/custom/flower_basket"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [2, 7, -1],
|
||||
"to": [14, 7, 11],
|
||||
"rotation": {"angle": 45, "axis": "x", "origin": [8, 7, 5]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"up": {"uv": [0, 16, 4, 12], "texture": "#1"},
|
||||
"down": {"uv": [0, 12, 4, 16], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 4, 12.5],
|
||||
"to": [14, 16, 12.5],
|
||||
"rotation": {"angle": -22.5, "axis": "x", "origin": [8, 10, 12.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"east": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"south": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"west": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"up": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"down": {"uv": [4, 12, 8, 8], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 7, 5],
|
||||
"to": [14, 7, 17],
|
||||
"rotation": {"angle": -45, "axis": "x", "origin": [8, 7, 11]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"up": {"uv": [0, 12, 4, 16], "texture": "#1"},
|
||||
"down": {"uv": [0, 16, 4, 12], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-1, 7, 2],
|
||||
"to": [11, 7, 14],
|
||||
"rotation": {"angle": -45, "axis": "z", "origin": [5, 7, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"up": {"uv": [4, 16, 0, 12], "rotation": 270, "texture": "#1"},
|
||||
"down": {"uv": [0, 16, 4, 12], "rotation": 270, "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 7, 2],
|
||||
"to": [17, 7, 14],
|
||||
"rotation": {"angle": 45, "axis": "z", "origin": [11, 7, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"up": {"uv": [4, 12, 0, 16], "rotation": 270, "texture": "#1"},
|
||||
"down": {"uv": [0, 12, 4, 16], "rotation": 270, "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [12.5, 4, 2],
|
||||
"to": [12.5, 16, 14],
|
||||
"rotation": {"angle": 22.5, "axis": "z", "origin": [12.5, 10, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"east": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"south": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"west": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"up": {"uv": [4, 8, 0, 12], "rotation": 270, "texture": "#1"},
|
||||
"down": {"uv": [0, 8, 4, 12], "rotation": 270, "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3.5, 7.75, 3.5],
|
||||
"to": [12.5, 9.25, 12.5],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.1875, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"},
|
||||
"east": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"},
|
||||
"south": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"},
|
||||
"west": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"},
|
||||
"up": {"uv": [13.5, 12.5, 10.5, 9.5], "texture": "#1"},
|
||||
"down": {"uv": [13.5, 12.5, 10.5, 15.5], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [4.1, 5.35, 4.1],
|
||||
"to": [11.9, 9.4, 11.9],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8.6875, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"},
|
||||
"east": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"},
|
||||
"south": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"},
|
||||
"west": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"},
|
||||
"up": {"uv": [16, 8.5, 13.5, 6], "texture": "#1"},
|
||||
"down": {"uv": [16, 3.5, 13.5, 6], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, 1, 0.5],
|
||||
"to": [8, 13, 15.5],
|
||||
"rotation": {"angle": 45, "axis": "y", "origin": [8, 7, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [0, 4, 4, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [4, 4, 0, 0], "texture": "#1"},
|
||||
"up": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"down": {"uv": [0, 4, 0, 0], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, 1, 0.5],
|
||||
"to": [8, 13, 15.5],
|
||||
"rotation": {"angle": -45, "axis": "y", "origin": [8, 7, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [0, 4, 4, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [4, 4, 0, 0], "texture": "#1"},
|
||||
"up": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"down": {"uv": [0, 4, 0, 0], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, 1, 0.5],
|
||||
"to": [8, 13, 15.5],
|
||||
"rotation": {"angle": -45, "axis": "y", "origin": [8, 7, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [4, 4, 8, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [8, 4, 4, 0], "texture": "#1"},
|
||||
"up": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"down": {"uv": [0, 4, 0, 0], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, 1, 0.5],
|
||||
"to": [8, 13, 15.5],
|
||||
"rotation": {"angle": 45, "axis": "y", "origin": [8, 7, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [4, 4, 8, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [8, 4, 4, 0], "texture": "#1"},
|
||||
"up": {"uv": [0, 4, 0, 0], "texture": "#1"},
|
||||
"down": {"uv": [0, 4, 0, 0], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3.5, 4, 2],
|
||||
"to": [3.5, 16, 14],
|
||||
"rotation": {"angle": -22.5, "axis": "z", "origin": [3.5, 10, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"east": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"south": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"west": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"up": {"uv": [4, 12, 0, 8], "rotation": 270, "texture": "#1"},
|
||||
"down": {"uv": [0, 12, 4, 8], "rotation": 270, "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-1, 6.25, 2],
|
||||
"to": [11, 6.25, 14],
|
||||
"rotation": {"angle": -45, "axis": "z", "origin": [5, 6.25, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"up": {"uv": [10, 8, 6, 4], "rotation": 270, "texture": "#1"},
|
||||
"down": {"uv": [6, 8, 10, 4], "rotation": 270, "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 6.25, 2],
|
||||
"to": [17, 6.25, 14],
|
||||
"rotation": {"angle": 45, "axis": "z", "origin": [11, 6.25, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"up": {"uv": [10, 4, 6, 8], "rotation": 270, "texture": "#1"},
|
||||
"down": {"uv": [6, 4, 10, 8], "rotation": 270, "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 6.25, -1],
|
||||
"to": [14, 6.25, 11],
|
||||
"rotation": {"angle": 45, "axis": "x", "origin": [8, 6.25, 5]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#1"},
|
||||
"up": {"uv": [6, 8, 10, 4], "texture": "#1"},
|
||||
"down": {"uv": [6, 4, 10, 8], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 6.25, 5],
|
||||
"to": [14, 6.25, 17],
|
||||
"rotation": {"angle": -45, "axis": "x", "origin": [8, 6.25, 11]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#1"},
|
||||
"up": {"uv": [6, 4, 10, 8], "texture": "#1"},
|
||||
"down": {"uv": [6, 8, 10, 4], "texture": "#1"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 4, 3.5],
|
||||
"to": [14, 16, 3.5],
|
||||
"rotation": {"angle": 22.5, "axis": "x", "origin": [8, 10, 3.5]},
|
||||
"faces": {
|
||||
"north": {"uv": [8, 8, 4, 12], "texture": "#1"},
|
||||
"east": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"south": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"west": {"uv": [4, 8, 8, 12], "texture": "#1"},
|
||||
"up": {"uv": [4, 12, 8, 8], "texture": "#1"},
|
||||
"down": {"uv": [4, 8, 8, 12], "texture": "#1"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [60, -34, 0],
|
||||
"translation": [-0.5, 3, 1],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [60, -34, 0],
|
||||
"translation": [-0.5, 3, 1],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"translation": [3.5, 0, 0]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"translation": [3.5, 0, 0]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3.25, 0],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [25, 135, 0],
|
||||
"translation": [0, -0.25, 0],
|
||||
"scale": [0.9, 0.9, 0.9]
|
||||
},
|
||||
"fixed": {
|
||||
"rotation": [90, 0, 0],
|
||||
"translation": [0, 0, -15],
|
||||
"scale": [2, 2, 2]
|
||||
}
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"name": "bone3",
|
||||
"origin": [-3, 4, -3],
|
||||
"color": 0,
|
||||
"children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"textures": {
|
||||
"0": "item/custom/flower_basket"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [2, 6.75, -1],
|
||||
"to": [14, 6.75, 11],
|
||||
"rotation": {"angle": -45, "axis": "x", "origin": [8, 6.75, 5]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [0, 16, 4, 12], "texture": "#0"},
|
||||
"down": {"uv": [0, 12, 4, 16], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 8.25, -1],
|
||||
"to": [14, 8.25, 11],
|
||||
"rotation": {"angle": -45, "axis": "x", "origin": [8, 8.25, 5]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [0, 12, 4, 8], "texture": "#0"},
|
||||
"down": {"uv": [0, 8, 4, 12], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 8.25, 5],
|
||||
"to": [14, 8.25, 17],
|
||||
"rotation": {"angle": 45, "axis": "x", "origin": [8, 8.25, 11]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [0, 8, 4, 12], "texture": "#0"},
|
||||
"down": {"uv": [0, 12, 4, 8], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 6.75, 5],
|
||||
"to": [14, 6.75, 17],
|
||||
"rotation": {"angle": 45, "axis": "x", "origin": [8, 6.75, 11]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [0, 12, 4, 16], "texture": "#0"},
|
||||
"down": {"uv": [0, 16, 4, 12], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-1, 6.75, 2],
|
||||
"to": [11, 6.75, 14],
|
||||
"rotation": {"angle": 45, "axis": "z", "origin": [5, 6.75, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 16, 0, 12], "rotation": 270, "texture": "#0"},
|
||||
"down": {"uv": [0, 16, 4, 12], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 6.75, 2],
|
||||
"to": [17, 6.75, 14],
|
||||
"rotation": {"angle": -45, "axis": "z", "origin": [11, 6.75, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 12, 0, 16], "rotation": 270, "texture": "#0"},
|
||||
"down": {"uv": [0, 12, 4, 16], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 8.25, 2],
|
||||
"to": [17, 8.25, 14],
|
||||
"rotation": {"angle": -45, "axis": "z", "origin": [11, 8.25, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 8, 0, 12], "rotation": 270, "texture": "#0"},
|
||||
"down": {"uv": [0, 8, 4, 12], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-1, 8.25, 2],
|
||||
"to": [11, 8.25, 14],
|
||||
"rotation": {"angle": 45, "axis": "z", "origin": [5, 8.25, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 12, 0, 8], "rotation": 270, "texture": "#0"},
|
||||
"down": {"uv": [0, 12, 4, 8], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3.5, 3.75, 3.5],
|
||||
"to": [12.5, 5.25, 12.5],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [4.25, 3.75, 4.25]},
|
||||
"faces": {
|
||||
"north": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"},
|
||||
"east": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"},
|
||||
"south": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"},
|
||||
"west": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"},
|
||||
"up": {"uv": [13.5, 12.5, 10.5, 9.5], "texture": "#0"},
|
||||
"down": {"uv": [13.5, 12.5, 10.5, 15.5], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [4.25, 0, 4.25],
|
||||
"to": [11.75, 3.75, 11.75],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [5, 0, 5]},
|
||||
"faces": {
|
||||
"north": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"},
|
||||
"east": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"},
|
||||
"south": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"},
|
||||
"west": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"},
|
||||
"up": {"uv": [16, 14.75, 13.5, 12.25], "texture": "#0"},
|
||||
"down": {"uv": [16, 9.75, 13.5, 12.25], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, 0, 0.5],
|
||||
"to": [8, 12, 15.5],
|
||||
"rotation": {"angle": 45, "axis": "y", "origin": [8, 6, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 4], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 4], "texture": "#0"},
|
||||
"up": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"down": {"uv": [0, 0, 0, 4], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, 0, 0.5],
|
||||
"to": [8, 12, 15.5],
|
||||
"rotation": {"angle": -45, "axis": "y", "origin": [8, 6, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 4], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 4], "texture": "#0"},
|
||||
"up": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"down": {"uv": [0, 0, 0, 4], "texture": "#0"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [60, -34, 0],
|
||||
"translation": [-0.5, 3, 1],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [60, -34, 0],
|
||||
"translation": [-0.5, 3, 1],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"translation": [3.5, 0, 0]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"translation": [3.5, 0, 0]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3.25, 0],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [25, -40, 0],
|
||||
"translation": [-0.25, 2, 0],
|
||||
"scale": [0.9, 0.9, 0.9]
|
||||
},
|
||||
"fixed": {
|
||||
"rotation": [-90, 0, 0],
|
||||
"translation": [0, 0, -16],
|
||||
"scale": [2, 2, 2]
|
||||
}
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"name": "bone",
|
||||
"origin": [-3, 0, -3],
|
||||
"color": 0,
|
||||
"children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"format_version": "1.21.6",
|
||||
"credit": "Made with Blockbench",
|
||||
"textures": {
|
||||
"0": "item/custom/flower_basket"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [-1, 11.5625, 4.43375],
|
||||
"to": [11, 11.5625, 13.43375],
|
||||
"rotation": {"angle": 45, "axis": "z", "origin": [5, 11.5625, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [3, 4, 0, 8], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [3, 8, 0, 4], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [-1, 10.0625, 4.43375],
|
||||
"to": [11, 10.0625, 13.43375],
|
||||
"rotation": {"angle": 45, "axis": "z", "origin": [5, 10.0625, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [6, 4, 3, 8], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [6, 8, 3, 4], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 11.5625, 4.43375],
|
||||
"to": [17, 11.5625, 13.43375],
|
||||
"rotation": {"angle": -45, "axis": "z", "origin": [11, 11.5625, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [3, 8, 0, 4], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [3, 4, 0, 8], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 10.0625, 4.43375],
|
||||
"to": [17, 10.0625, 13.43375],
|
||||
"rotation": {"angle": -45, "axis": "z", "origin": [11, 10.0625, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [6, 8, 3, 4], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [6, 4, 3, 8], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 10.0625, 4.43375],
|
||||
"to": [14, 10.0625, 16.43375],
|
||||
"rotation": {"angle": 45, "axis": "x", "origin": [8, 10.0625, 10.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 12, 0, 16], "texture": "#0"},
|
||||
"down": {"uv": [0, 12, 4, 16], "rotation": 180, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 10.0625, 1.43375],
|
||||
"to": [14, 10.0625, 13.43375],
|
||||
"rotation": {"angle": -45, "axis": "x", "origin": [8, 10.0625, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 16, 0, 12], "texture": "#0"},
|
||||
"down": {"uv": [0, 16, 4, 12], "rotation": 180, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11.5625, 2.18375],
|
||||
"to": [14, 11.5625, 14.18375],
|
||||
"rotation": {"angle": -45, "axis": "x", "origin": [8, 11.5625, 8.18375]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 4, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 12, 0, 8], "texture": "#0"},
|
||||
"down": {"uv": [0, 12, 4, 8], "rotation": 180, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 11.5625, 3.68375],
|
||||
"to": [14, 11.5625, 15.68375],
|
||||
"rotation": {"angle": 45, "axis": "x", "origin": [8, 11.5625, 9.68375]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"east": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"south": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"west": {"uv": [4, 0, 0, 0], "texture": "#0"},
|
||||
"up": {"uv": [4, 8, 0, 12], "texture": "#0"},
|
||||
"down": {"uv": [0, 8, 4, 12], "rotation": 180, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3.5, 7.0625, 5.93375],
|
||||
"to": [12.5, 8.5625, 11.93375],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [13.5, 15.5, 10.5, 16], "texture": "#0"},
|
||||
"east": {"uv": [9, 15.5, 7, 16], "texture": "#0"},
|
||||
"south": {"uv": [13.5, 15.5, 10.5, 16], "texture": "#0"},
|
||||
"west": {"uv": [9, 15.5, 7, 16], "texture": "#0"},
|
||||
"up": {"uv": [11.5, 6.5, 13.5, 3.5], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [11.5, 6.5, 13.5, 9.5], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [4.25, 3.3125, 6.68375],
|
||||
"to": [11.75, 7.0625, 11.18375],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [16, 14.75, 13.5, 16], "texture": "#0"},
|
||||
"east": {"uv": [10.5, 14.75, 9, 16], "texture": "#0"},
|
||||
"south": {"uv": [16, 14.75, 13.5, 16], "texture": "#0"},
|
||||
"west": {"uv": [10.5, 14.75, 9, 16], "texture": "#0"},
|
||||
"up": {"uv": [8, 14.75, 10.5, 12.25], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [9, 9.75, 10.5, 12.25], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [4.1, 3.1625, 6.53375],
|
||||
"to": [11.9, 7.2125, 11.33375],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 7.43375]},
|
||||
"faces": {
|
||||
"north": {"uv": [16, 8.5, 13.5, 9.75], "texture": "#0"},
|
||||
"east": {"uv": [16, 8.5, 14.5, 9.75], "texture": "#0"},
|
||||
"south": {"uv": [16, 8.5, 13.5, 9.75], "texture": "#0"},
|
||||
"west": {"uv": [14.5, 8.5, 16, 9.75], "texture": "#0"},
|
||||
"up": {"uv": [13.5, 8.5, 16, 6], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [15, 3.5, 13.5, 6], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0.5, 3.3125, 8.93375],
|
||||
"to": [15.5, 15.3125, 8.93375],
|
||||
"rotation": {"angle": -22.5, "axis": "y", "origin": [8, 9.3125, 8.93375]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 4], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 4], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"up": {"uv": [0, 0, 0, 4], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [0, 0, 0, 4], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0.5, 3.3125, 8.93375],
|
||||
"to": [15.5, 15.3125, 8.93375],
|
||||
"rotation": {"angle": 22.5, "axis": "y", "origin": [8, 9.3125, 8.93375]},
|
||||
"faces": {
|
||||
"north": {"uv": [4, 0, 0, 4], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 4, 4], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 0, 4], "texture": "#0"},
|
||||
"up": {"uv": [0, 0, 0, 4], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [0, 0, 0, 4], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 3.3125, 4.44125],
|
||||
"to": [11, 5.5625, 6.69125],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [10.25, 3.3125, 5.93375]},
|
||||
"faces": {
|
||||
"north": {"uv": [16, 0, 14, 0.75], "texture": "#0"},
|
||||
"east": {"uv": [14, 2.75, 13.25, 3.5], "texture": "#0"},
|
||||
"south": {"uv": [16, 0.75, 14, 1.5], "texture": "#0"},
|
||||
"west": {"uv": [13.25, 2.75, 14, 3.5], "texture": "#0"},
|
||||
"up": {"uv": [13.25, 0, 12.5, 2], "rotation": 90, "texture": "#0"},
|
||||
"down": {"uv": [14, 0, 13.25, 2], "rotation": 270, "texture": "#0"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [60, -34, 0],
|
||||
"translation": [-0.5, 3, 1],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [60, -34, 0],
|
||||
"translation": [-0.5, 3, 1],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"translation": [3.5, 0, 0]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"translation": [3.5, 0, 0]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3.25, 0],
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [25, 135, 0],
|
||||
"translation": [-0.25, 2, 0],
|
||||
"scale": [0.9, 0.9, 0.9]
|
||||
},
|
||||
"fixed": {
|
||||
"rotation": [-90, 0, 0],
|
||||
"translation": [0, 0, -16],
|
||||
"scale": [2, 2, 2]
|
||||
}
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"name": "bone2",
|
||||
"origin": [-5, 5.3125, -4.43375],
|
||||
"color": 0,
|
||||
"children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 547 B |
@@ -59,12 +59,18 @@ images:
|
||||
font: minecraft:gui
|
||||
file: minecraft:font/gui/custom/smithing_transform_recipe.png
|
||||
char: \ub009
|
||||
internal:brewing_recipe:
|
||||
height: 142
|
||||
ascent: 20
|
||||
font: minecraft:gui
|
||||
file: minecraft:font/gui/custom/brewing_recipe.png
|
||||
char: \ub00a
|
||||
internal:no_recipe:
|
||||
height: 140
|
||||
ascent: 18
|
||||
font: minecraft:gui
|
||||
file: minecraft:font/gui/custom/no_recipe.png
|
||||
char: \ub00a
|
||||
char: \ub00b
|
||||
templates:
|
||||
internal:icon/2d:
|
||||
material: arrow
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -49,11 +49,11 @@ command.item.get.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:0
|
||||
command.item.give.success.single: "<lang:commands.give.success.single:'<arg:0>':'<arg:1>':'<arg:2>'>"
|
||||
command.item.give.success.multiple: "<lang:commands.give.success.multiple:'<arg:0>':'<arg:1>':'<arg:2>'>"
|
||||
command.item.give.failure.not_exist: "<red><lang:argument.item.id.invalid:'<arg:0>'></red>"
|
||||
command.search_recipe.not_found: "<red>Kein Rezept für diesen Gegenstand gefunden</red>"
|
||||
command.search_usage.not_found: "<red>Keine Verwendung für diesen Gegenstand gefunden</red>"
|
||||
command.search_recipe.no_item: "<red>Bitte halten Sie einen Gegenstand, bevor Sie diesen Befehl ausführen</red>"
|
||||
command.search_usage.no_item: "<red>Bitte halten Sie einen Gegenstand, bevor Sie diesen Befehl ausführen</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>Gegenstand '<arg:0>' ist kein minecraft:totem_of_undying</red>"
|
||||
command.search_recipe.not_found: "<red>Kein Rezept für diesen Item gefunden</red>"
|
||||
command.search_usage.not_found: "<red>Keine Verwendung für dieses Item gefunden</red>"
|
||||
command.search_recipe.no_item: "<red>Bitte halten Sie ein Item, bevor Sie diesen Befehl ausführen</red>"
|
||||
command.search_usage.no_item: "<red>Bitte halten Sie ein Item, bevor Sie diesen Befehl ausführen</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>Item '<arg:0>' ist kein minecraft:totem_of_undying</red>"
|
||||
command.resource.enable.success: "<white>Ressource <arg:0> aktiviert. Führen Sie <click:run_command:/ce reload all><u>/ce reload all</u></click> aus, um die Änderungen anzuwenden</white>"
|
||||
command.resource.enable.failure.unknown: "<red>Unbekannte Ressource <arg:0></red>"
|
||||
command.resource.disable.success: "<white>Ressource <arg:0> deaktiviert. Führen Sie <click:run_command:/ce reload all><u>/ce reload all</u></click> aus, um die Änderungen anzuwenden</white>"
|
||||
@@ -65,6 +65,7 @@ command.send_resource_pack.success.single: "<white>Ressourcenpaket an <arg:0> ge
|
||||
command.send_resource_pack.success.multiple: "<white>Ressourcenpakete an <arg:0> Spieler gesendet.</white>"
|
||||
warning.config.pack.duplicated_files: "</red>Duplizierte Dateien gefunden. Bitte beheben Sie diese im Abschnitt 'resource-pack.duplicated-files-handler' in config.yml.</red>"
|
||||
warning.config.yaml.duplicated_key: "<red>Problem in Datei <arg:0> gefunden - Duplizierter Schlüssel '<arg:1>' in Zeile <arg:2> gefunden, dies kann zu unerwarteten Ergebnissen führen.</red>"
|
||||
warning.config.yaml.inconsistent_value_type: "<red>Fehler in der Datei <arg:0> - Der duplizierte Schlüssel '<arg:1>' wurde in Zeile <arg:2> mit einem anderen Werttyp gefunden. Dies könnte zu unerwarteten Ergebnissen führen.</red>"
|
||||
warning.config.type.int: "<yellow>Problem in Datei <arg:0> gefunden - Laden von '<arg:1>' fehlgeschlagen: Kann '<arg:2>' nicht in den Integer-Typ für Option '<arg:3>' umwandeln.</yellow>"
|
||||
warning.config.type.float: "<yellow>Problem in Datei <arg:0> gefunden - Laden von '<arg:1>' fehlgeschlagen: Kann '<arg:2>' nicht in den Float-Typ für Option '<arg:3>' umwandeln.</yellow>"
|
||||
warning.config.type.double: "<yellow>Problem in Datei <arg:0> gefunden - Laden von '<arg:1>' fehlgeschlagen: Kann '<arg:2>' nicht in den Double-Typ für Option '<arg:3>' umwandeln.</yellow>"
|
||||
@@ -154,72 +155,72 @@ warning.config.furniture.element.missing_item: "<yellow>Problem in Datei <arg:0>
|
||||
warning.config.furniture.settings.unknown: "<yellow>Problem in Datei <arg:0> gefunden - Das Möbelstück '<arg:1>' verwendet einen unbekannten Einstellungstyp '<arg:2>'.</yellow>"
|
||||
warning.config.furniture.hitbox.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Möbelstück '<arg:1>' verwendet einen ungültigen Hitbox-Typ '<arg:2>'.</yellow>"
|
||||
warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>Problem in Datei <arg:0> gefunden - Das Möbelstück '<arg:1>' verwendet eine benutzerdefinierte Hitbox mit ungültigem Entitätstyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.duplicate: "<yellow>Problem in Datei <arg:0> gefunden - Duplizierter Gegenstand '<arg:1>'. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist.</yellow>"
|
||||
warning.config.item.settings.unknown: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen unbekannten Einstellungstyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.settings.invulnerable.invalid_damage_source: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet eine unbekannte Schadensquelle '<arg:2>'. Erlaubte Quellen: [<arg:3>].</yellow>"
|
||||
warning.config.item.settings.equippable.missing_slot: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'slot'-Argument für die 'equippable'-Einstellung.</yellow>"
|
||||
warning.config.item.missing_material: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'material'-Argument.</yellow>"
|
||||
warning.config.item.invalid_material: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen Materialtyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.invalid_custom_model_data: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet negative benutzerdefinierte Modelldaten '<arg:2>', die ungültig sind.</yellow>"
|
||||
warning.config.item.bad_custom_model_data: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet benutzerdefinierte Modelldaten '<arg:2>', die zu groß sind. Es wird empfohlen, einen Wert unter 16.777.216 zu verwenden.</yellow>"
|
||||
warning.config.item.custom_model_data_conflict: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet benutzerdefinierte Modelldaten '<arg:2>', die bereits von Gegenstand '<arg:3>' belegt sind.</yellow>"
|
||||
warning.config.item.invalid_component: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen nicht existierenden Komponententyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.missing_model_id: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'custom-model-data' oder 'item-model'-Argument.</yellow>"
|
||||
warning.config.item.missing_model: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt der erforderliche 'model'-Abschnitt für die Unterstützung von 1.21.4+-Ressourcenpaketen.</yellow>"
|
||||
warning.config.item.behavior.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'type'-Argument für sein Gegenstandsverhalten.</yellow>"
|
||||
warning.config.item.behavior.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen Gegenstandsverhaltenstyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.behavior.block.missing_block: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'block'-Argument für das 'block_item'-Verhalten.</yellow>"
|
||||
warning.config.item.behavior.furniture.missing_furniture: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'furniture'-Argument für das 'furniture_item'-Verhalten.</yellow>"
|
||||
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'block'-Argument für das 'liquid_collision_block_item'-Verhalten.</yellow>"
|
||||
warning.config.item.legacy_model.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'path'-Argument für das Legacy-Modell.</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'path'-Argument für Legacy-Modell-Überschreibungen.</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_predicate: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'predicate'-Argument für Legacy-Modell-Überschreibungen.</yellow>"
|
||||
warning.config.item.legacy_model.cannot_convert: "<yellow>Problem in Datei <arg:0> gefunden - Kann 1.21.4+-Gegenstände für Gegenstand '<arg:1>' nicht in das Legacy-Format konvertieren. Bitte erstellen Sie den Abschnitt 'legacy-model' für diesen Gegenstand manuell.</yellow>"
|
||||
warning.config.item.model.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen Modelltyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.model.tint.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'type'-Argument für die Tönung.</yellow>"
|
||||
warning.config.item.model.tint.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen Tönungstyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.model.tint.constant.missing_value: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'value'-Argument für die konstante Tönung.</yellow>"
|
||||
warning.config.item.model.tint.grass.invalid_temp: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet eine ungültige Temperatur '<arg:2>' für die Grastönung, die zwischen 0 und 1 liegen sollte.</yellow>"
|
||||
warning.config.item.model.tint.grass.invalid_downfall: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen Niederschlag '<arg:2>' für die Grastönung, der zwischen 0 und 1 liegen sollte.</yellow>"
|
||||
warning.config.item.model.tint.invalid_value: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet eine ungültige Tönung '<arg:2>'.</yellow>"
|
||||
warning.config.item.model.base.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:model'.</yellow>"
|
||||
warning.config.item.model.base.invalid_path: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' hat ein ungültiges 'path'-Argument '<arg:2>' für das Modell 'minecraft:model', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
|
||||
warning.config.item.model.condition.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.invalid_property: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet eine ungültige Eigenschaft '<arg:2>' für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.missing_on_true: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'on-true'-Argument für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.missing_on_false: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'on-false'-Argument für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.keybind.missing_keybind: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'.</yellow>"
|
||||
warning.config.item.model.condition.component.missing_predicate: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'predicate'-Argument für die Eigenschaft 'minecraft:has_component'.</yellow>"
|
||||
warning.config.item.model.condition.component.missing_value: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'value'-Argument für die Eigenschaft 'minecraft:has_component'.</yellow>"
|
||||
warning.config.item.model.condition.has_component.missing_component: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:has_component'.</yellow>"
|
||||
warning.config.item.model.composite.missing_models: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'models'-Argument für das Modell 'minecraft:composite'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:range_dispatch'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.invalid_property: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet eine ungültige Eigenschaft '<arg:2>' für das Modell 'minecraft:range_dispatch'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.missing_entries: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'entries'-Argument für das Modell 'minecraft:composite'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.entry.missing_model: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'model'-Argument für einen der Einträge im Modell 'minecraft:composite'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.compass.missing_target: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'target'-Argument für die Eigenschaft 'minecraft:compass'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.time.missing_source: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'source'-Argument für die Eigenschaft 'minecraft:time'.</yellow>"
|
||||
warning.config.item.model.select.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.invalid_property: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet eine ungültige Eigenschaft '<arg:2>' für das Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.missing_cases: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'cases'-Argument für das Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.case.missing_when: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'when'-Argument für einen der Fälle im Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.case.missing_model: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'model'-Argument für einen der Fälle im Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.component.missing_component: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:component'.</yellow>"
|
||||
warning.config.item.model.select.block_state.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'block-state-property'-Argument für die Eigenschaft 'minecraft:block_state'.</yellow>"
|
||||
warning.config.item.model.select.local_time.missing_pattern: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'pattern'-Argument für die Eigenschaft 'minecraft:local_time'.</yellow>"
|
||||
warning.config.item.model.special.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'type'-Argument für das Modell 'minecraft:special'.</yellow>"
|
||||
warning.config.item.model.special.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:special'.</yellow>"
|
||||
warning.config.item.model.special.invalid_path: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' hat ein ungültiges 'path'-Argument '<arg:2>' für das Modell 'minecraft:special', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
|
||||
warning.config.item.model.special.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen Typ '<arg:2>' für das Modell 'minecraft:special'.</yellow>"
|
||||
warning.config.item.model.special.banner.missing_color: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'color'-Argument für das Spezialmodell 'minecraft:banner'.</yellow>"
|
||||
warning.config.item.model.special.bed.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:bed'.</yellow>"
|
||||
warning.config.item.model.special.sign.missing_wood_type: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'wood-type'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'.</yellow>"
|
||||
warning.config.item.model.special.sign.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'.</yellow>"
|
||||
warning.config.item.model.special.chest.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:chest'.</yellow>"
|
||||
warning.config.item.model.special.chest.invalid_openness: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen 'openness'-Wert '<arg:2>' für das Spezialmodell 'minecraft:chest'. Gültiger Bereich '0~1.'</yellow>"
|
||||
warning.config.item.model.special.shulker_box.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:shulker_box'.</yellow>"
|
||||
warning.config.item.model.special.shulker_box.invalid_openness: "<yellow>Problem in Datei <arg:0> gefunden - Der Gegenstand '<arg:1>' verwendet einen ungültigen 'openness'-Wert '<arg:2>' für das Spezialmodell 'minecraft:shulker_box'. Gültiger Bereich '0~1.'</yellow>"
|
||||
warning.config.item.model.special.head.missing_kind: "<yellow>Problem in Datei <arg:0> gefunden - Dem Gegenstand '<arg:1>' fehlt das erforderliche 'kind'-Argument für das Spezialmodell 'minecraft:head'.</yellow>"
|
||||
warning.config.item.duplicate: "<yellow>Problem in Datei <arg:0> gefunden - Dupliziertes Item '<arg:1>'. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist.</yellow>"
|
||||
warning.config.item.settings.unknown: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen unbekannten Einstellungstyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.settings.invulnerable.invalid_damage_source: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet eine unbekannte Schadensquelle '<arg:2>'. Erlaubte Quellen: [<arg:3>].</yellow>"
|
||||
warning.config.item.settings.equippable.missing_slot: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'slot'-Argument für die 'equippable'-Einstellung.</yellow>"
|
||||
warning.config.item.missing_material: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'material'-Argument.</yellow>"
|
||||
warning.config.item.invalid_material: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen Materialtyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.invalid_custom_model_data: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet negative benutzerdefinierte Modelldaten '<arg:2>', die ungültig sind.</yellow>"
|
||||
warning.config.item.bad_custom_model_data: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet benutzerdefinierte Modelldaten '<arg:2>', die zu groß sind. Es wird empfohlen, einen Wert unter 16.777.216 zu verwenden.</yellow>"
|
||||
warning.config.item.custom_model_data_conflict: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet benutzerdefinierte Modelldaten '<arg:2>', die bereits von Item '<arg:3>' belegt sind.</yellow>"
|
||||
warning.config.item.invalid_component: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen nicht existierenden Komponententyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.missing_model_id: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'custom-model-data' oder 'item-model'-Argument.</yellow>"
|
||||
warning.config.item.missing_model: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt der erforderliche 'model'-Abschnitt für die Unterstützung von 1.21.4+-Ressourcenpaketen.</yellow>"
|
||||
warning.config.item.behavior.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'type'-Argument für sein Itemsverhalten.</yellow>"
|
||||
warning.config.item.behavior.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen Itemsverhaltenstyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.behavior.block.missing_block: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'block'-Argument für das 'block_item'-Verhalten.</yellow>"
|
||||
warning.config.item.behavior.furniture.missing_furniture: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'furniture'-Argument für das 'furniture_item'-Verhalten.</yellow>"
|
||||
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'block'-Argument für das 'liquid_collision_block_item'-Verhalten.</yellow>"
|
||||
warning.config.item.legacy_model.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'path'-Argument für das Legacy-Modell.</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'path'-Argument für Legacy-Modell-Überschreibungen.</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_predicate: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'predicate'-Argument für Legacy-Modell-Überschreibungen.</yellow>"
|
||||
warning.config.item.legacy_model.cannot_convert: "<yellow>Problem in Datei <arg:0> gefunden - Kann 1.21.4+-Gegenstände für Item '<arg:1>' nicht in das Legacy-Format konvertieren. Bitte erstellen Sie den Abschnitt 'legacy-model' für diesen Item manuell.</yellow>"
|
||||
warning.config.item.model.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen Modelltyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.model.tint.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'type'-Argument für die Tönung.</yellow>"
|
||||
warning.config.item.model.tint.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen Tönungstyp '<arg:2>'.</yellow>"
|
||||
warning.config.item.model.tint.constant.missing_value: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'value'-Argument für die konstante Tönung.</yellow>"
|
||||
warning.config.item.model.tint.grass.invalid_temp: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet eine ungültige Temperatur '<arg:2>' für die Grastönung, die zwischen 0 und 1 liegen sollte.</yellow>"
|
||||
warning.config.item.model.tint.grass.invalid_downfall: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen Niederschlag '<arg:2>' für die Grastönung, der zwischen 0 und 1 liegen sollte.</yellow>"
|
||||
warning.config.item.model.tint.invalid_value: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet eine ungültige Tönung '<arg:2>'.</yellow>"
|
||||
warning.config.item.model.base.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:model'.</yellow>"
|
||||
warning.config.item.model.base.invalid_path: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' hat ein ungültiges 'path'-Argument '<arg:2>' für das Modell 'minecraft:model', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
|
||||
warning.config.item.model.condition.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.invalid_property: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet eine ungültige Eigenschaft '<arg:2>' für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.missing_on_true: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'on-true'-Argument für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.missing_on_false: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'on-false'-Argument für das Modell 'minecraft:condition'.</yellow>"
|
||||
warning.config.item.model.condition.keybind.missing_keybind: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'.</yellow>"
|
||||
warning.config.item.model.condition.component.missing_predicate: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'predicate'-Argument für die Eigenschaft 'minecraft:has_component'.</yellow>"
|
||||
warning.config.item.model.condition.component.missing_value: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'value'-Argument für die Eigenschaft 'minecraft:has_component'.</yellow>"
|
||||
warning.config.item.model.condition.has_component.missing_component: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:has_component'.</yellow>"
|
||||
warning.config.item.model.composite.missing_models: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'models'-Argument für das Modell 'minecraft:composite'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:range_dispatch'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.invalid_property: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet eine ungültige Eigenschaft '<arg:2>' für das Modell 'minecraft:range_dispatch'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.missing_entries: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'entries'-Argument für das Modell 'minecraft:composite'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.entry.missing_model: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'model'-Argument für einen der Einträge im Modell 'minecraft:composite'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.compass.missing_target: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'target'-Argument für die Eigenschaft 'minecraft:compass'.</yellow>"
|
||||
warning.config.item.model.range_dispatch.time.missing_source: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'source'-Argument für die Eigenschaft 'minecraft:time'.</yellow>"
|
||||
warning.config.item.model.select.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.invalid_property: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet eine ungültige Eigenschaft '<arg:2>' für das Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.missing_cases: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'cases'-Argument für das Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.case.missing_when: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'when'-Argument für einen der Fälle im Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.case.missing_model: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'model'-Argument für einen der Fälle im Modell 'minecraft:select'.</yellow>"
|
||||
warning.config.item.model.select.component.missing_component: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:component'.</yellow>"
|
||||
warning.config.item.model.select.block_state.missing_property: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'block-state-property'-Argument für die Eigenschaft 'minecraft:block_state'.</yellow>"
|
||||
warning.config.item.model.select.local_time.missing_pattern: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'pattern'-Argument für die Eigenschaft 'minecraft:local_time'.</yellow>"
|
||||
warning.config.item.model.special.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'type'-Argument für das Modell 'minecraft:special'.</yellow>"
|
||||
warning.config.item.model.special.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:special'.</yellow>"
|
||||
warning.config.item.model.special.invalid_path: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' hat ein ungültiges 'path'-Argument '<arg:2>' für das Modell 'minecraft:special', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
|
||||
warning.config.item.model.special.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen Typ '<arg:2>' für das Modell 'minecraft:special'.</yellow>"
|
||||
warning.config.item.model.special.banner.missing_color: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'color'-Argument für das Spezialmodell 'minecraft:banner'.</yellow>"
|
||||
warning.config.item.model.special.bed.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:bed'.</yellow>"
|
||||
warning.config.item.model.special.sign.missing_wood_type: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'wood-type'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'.</yellow>"
|
||||
warning.config.item.model.special.sign.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'.</yellow>"
|
||||
warning.config.item.model.special.chest.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:chest'.</yellow>"
|
||||
warning.config.item.model.special.chest.invalid_openness: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen 'openness'-Wert '<arg:2>' für das Spezialmodell 'minecraft:chest'. Gültiger Bereich '0~1.'</yellow>"
|
||||
warning.config.item.model.special.shulker_box.missing_texture: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:shulker_box'.</yellow>"
|
||||
warning.config.item.model.special.shulker_box.invalid_openness: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' verwendet einen ungültigen 'openness'-Wert '<arg:2>' für das Spezialmodell 'minecraft:shulker_box'. Gültiger Bereich '0~1.'</yellow>"
|
||||
warning.config.item.model.special.head.missing_kind: "<yellow>Problem in Datei <arg:0> gefunden - Das Item '<arg:1>' fehlt das erforderliche 'kind'-Argument für das Spezialmodell 'minecraft:head'.</yellow>"
|
||||
warning.config.block.duplicate: "<yellow>Problem in Datei <arg:0> gefunden - Duplizierter Block '<arg:1>'. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist.</yellow>"
|
||||
warning.config.block.missing_state: "<yellow>Problem in Datei <arg:0> gefunden - Dem Block '<arg:1>' fehlt das erforderliche 'state'-Argument.</yellow>"
|
||||
warning.config.block.state.property.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Dem Block '<arg:1>' fehlt das erforderliche 'type'-Argument für die Eigenschaft '<arg:2>'.</yellow>"
|
||||
@@ -378,13 +379,41 @@ warning.config.function.remove_cooldown.missing_id: "<yellow>Problem in Datei <a
|
||||
warning.config.selector.missing_type: "<yellow>Problem in Datei <arg:0> gefunden - Der Konfiguration '<arg:1>' fehlt das erforderliche 'type'-Argument für den Selektor.</yellow>"
|
||||
warning.config.selector.invalid_type: "<yellow>Problem in Datei <arg:0> gefunden - Die Konfiguration '<arg:1>' verwendet einen ungültigen Selektortyp '<arg:2>'.</yellow>"
|
||||
warning.config.selector.invalid_target: "<yellow>Problem in Datei <arg:0> gefunden - Die Konfiguration '<arg:1>' verwendet ein ungültiges Selektorziel '<arg:2>'.</yellow>"
|
||||
warning.config.resource_pack.item_model.conflict.vanilla: "<yellow>Fehler beim Generieren des Gegenstandsmodells für '<arg:0>' da dieses Gegenstandsmodell von einem Vanilla-Gegenstand belegt wurde.</yellow>"
|
||||
warning.config.resource_pack.item_model.already_exist: "<yellow>Fehler beim Generieren des Gegenstandsmodells für '<arg:0>' da die Datei '<arg:1>' bereits existiert.</yellow>"
|
||||
warning.config.resource_pack.item_model.already_exist: "<yellow>Fehler beim Generieren des Itemsmodells für '<arg:0>' da die Datei '<arg:1>' bereits existiert.</yellow>"
|
||||
warning.config.resource_pack.model.generation.already_exist: "<yellow>Fehler beim Generieren des Modells, da die Modelldatei '<arg:0>' bereits existiert.</yellow>"
|
||||
warning.config.resource_pack.generation.missing_font_texture: "<yellow>Schriftart '<arg:0>' fehlt die erforderliche Textur: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.missing_model_texture: "<yellow>Modell '<arg:0>' fehlt Textur '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.missing_item_model: "<yellow>Gegenstand '<arg:0>' fehlt Modelldatei: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.missing_item_model: "<yellow>Item '<arg:0>' fehlt Modelldatei: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.missing_block_model: "<yellow>Block '<arg:0>' fehlt Modelldatei: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.missing_parent_model: "<yellow>Modell '<arg:0>' kann das Elternmodell nicht finden: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.malformatted_json: "<yellow>Json-Datei '<arg:0>' ist fehlerhaft formatiert.</yellow>"
|
||||
warning.config.resource_pack.invalid_overlay_format: "<yellow>Problem in config.yml gefunden - Ungültiges Overlay-Format '<arg:0>' im Abschnitt 'resource-pack'. Unterstützte Overlays: [<arg:1>]</yellow>"
|
||||
warning.config.type.map: "<yellow>Fehler in der Datei <arg:0> - Das Laden von '<arg:1>' ist fehlgeschlagen: Kann den Typ '<arg:2>' nicht in den Typ 'Map' für die Option '<arg:3>' umwandeln.</yellow>"
|
||||
warning.config.recipe.missing_pattern: "<yellow>Fehler in der Datei <arg:0> - Dem geformten Rezept '<arg:1>' fehlt das erforderliche Argument 'pattern'.</yellow>"
|
||||
warning.config.recipe.too_large_pattern: "<yellow>Fehler in der Datei <arg:0> - Das geformte Rezept '<arg:1>' hat ein Muster mit mehr als 3 Zeilen oder Spalten.</yellow>"
|
||||
warning.config.recipe.mismatched_shaped_pattern: "<yellow>Fehler in der Datei <arg:0> - Das geformte Rezept '<arg:1>' hat ein fehlerhaftes Muster und Zutaten.</yellow>"
|
||||
warning.config.recipe.mismatched_shulker_box: "<yellow>Fehler in der Datei <arg:0> - Das Shulker-Box-Rezept '<arg:1>' hat ein fehlerhaftes Muster und Zutaten.</yellow>"
|
||||
warning.config.recipe.mismatched_unshaped_pattern: "<yellow>Fehler in der Datei <arg:0> - Das ungeformte Rezept '<arg:1>' hat ein fehlerhaftes Muster und Zutaten.</yellow>"
|
||||
warning.config.recipe.missing_category: "<yellow>Fehler in der Datei <arg:0> - Dem Rezept '<arg:1>' fehlt das erforderliche 'category'-Argument.</yellow>"
|
||||
warning.config.recipe.missing_experience: "<yellow>Fehler in der Datei <arg:0> - Dem Kochrezept '<arg:1>' fehlt das erforderliche 'experience'-Argument.</yellow>"
|
||||
warning.config.recipe.missing_cooking_time: "<yellow>Fehler in der Datei <arg:0> - Dem Kochrezept '<arg:1>' fehlt das erforderliche 'cooking-time'-Argument.</yellow>"
|
||||
warning.config.recipe.missing_group: "<yellow>Fehler in der Datei <arg:0> - Dem Rezept '<arg:1>' fehlt das erforderliche 'group'-Argument.</yellow>"
|
||||
warning.config.recipe.missing_book: "<yellow>Fehler in der Datei <arg:0> - Dem Rezept '<arg:1>' fehlt das erforderliche 'book'-Argument.</yellow>"
|
||||
warning.config.loot_table.duplicate: "<yellow>Fehler in der Datei <arg:0> - Doppelte Loot-Tabelle '<arg:1>'. Bitte prüfen Sie, ob in anderen Dateien die gleiche Konfiguration vorhanden ist.</yellow>"
|
||||
warning.config.loot_table.missing_type: "<yellow>Fehler in der Datei <arg:0> - Der Loot-Tabelle '<arg:1>' fehlt das erforderliche 'type'-Argument.</yellow>"
|
||||
warning.config.loot_table.invalid_type: "<yellow>Fehler in der Datei <arg:0> - Die Loot-Tabelle '<arg:1>' verwendet einen ungültigen Loot-Tabellen-Typ '<arg:2>'.</yellow>"
|
||||
warning.config.loot_table.missing_rolls: "<yellow>Fehler in der Datei <arg:0> - Der Loot-Tabelle '<arg:1>' fehlt das erforderliche 'rolls'-Argument.</yellow>"
|
||||
warning.config.loot_table.missing_entries: "<yellow>Fehler in der Datei <arg:0> - Der Loot-Tabelle '<arg:1>' fehlt das erforderliche 'entries'-Argument.</yellow>"
|
||||
warning.config.loot_table.missing_entry_type: "<yellow>Fehler in der Datei <arg:0> - Einer der Loot-Tabellen-Einträge in '<arg:1>' hat kein erforderliches 'type'-Argument.</yellow>"
|
||||
warning.config.loot_table.invalid_entry_type: "<yellow>Fehler in der Datei <arg:0> - Einer der Loot-Tabellen-Einträge in '<arg:1>' verwendet einen ungültigen Typ '<arg:2>'.</yellow>"
|
||||
warning.config.loot_table.missing_entry_name: "<yellow>Fehler in der Datei <arg:0> - Einer der Loot-Tabellen-Einträge in '<arg:1>' hat kein erforderliches 'name'-Argument.</yellow>"
|
||||
warning.config.loot_table.missing_entry_weight: "<yellow>Fehler in der Datei <arg:0> - Einer der Loot-Tabellen-Einträge in '<arg:1>' hat kein erforderliches 'weight'-Argument.</yellow>"
|
||||
warning.config.loot_table.missing_function_type: "<yellow>Fehler in der Datei <arg:0> - Eine der Loot-Tabellen-Funktionen in '<arg:1>' hat kein erforderliches 'type'-Argument.</yellow>"
|
||||
warning.config.loot_table.invalid_function_type: "<yellow>Fehler in der Datei <arg:0> - Eine der Loot-Tabellen-Funktionen in '<arg:1>' verwendet einen ungültigen Typ '<arg:2>'.</yellow>"
|
||||
warning.config.loot_table.missing_condition_type: "<yellow>Fehler in der Datei <arg:0> - Eine der Loot-Tabellen-Bedingungen in '<arg:1>' hat kein erforderliches 'type'-Argument.</yellow>"
|
||||
warning.config.loot_table.invalid_condition_type: "<yellow>Fehler in der Datei <arg:0> - Eine der Loot-Tabellen-Bedingungen in '<arg:1>' verwendet einen ungültigen Typ '<arg:2>'.</yellow>"
|
||||
warning.config.resource_pack.generation.missing_equipment_texture: "Ausrüstung '<arg:0>' hat keine Textur '<arg:1>'"
|
||||
warning.config.equipment.duplicate: "Problem in der Datei <arg:0> gefunden – Duplizierte Ausrüstung '<arg:1>'. Bitte prüfe, ob es dieselbe Konfiguration in anderen Dateien gibt."
|
||||
warning.config.equipment.missing_type: "Problem in der Datei <arg:0> gefunden – Der Ausrüstung '<arg:1>' fehlt das erforderliche Argument 'type'."
|
||||
warning.config.equipment.invalid_type: "Problem in der Datei <arg:0> gefunden – Die Ausrüstung '<arg:1>' verwendet ein ungültiges Argument 'type'."
|
||||
warning.config.equipment.invalid_sacrificed_armor: "Problem in config.yml bei 'equipment.sacrificed-vanilla-armor' gefunden – Ungültiger Vanille-Rüstungstyp '<arg:0>'."
|
||||
@@ -72,6 +72,7 @@ warning.config.type.float: "<yellow>Issue found in file <arg:0> - Failed to load
|
||||
warning.config.type.double: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to double type for option '<arg:3>'.</yellow>"
|
||||
warning.config.type.quaternionf: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Quaternionf type for option '<arg:3>'.</yellow>"
|
||||
warning.config.type.vector3f: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Vector3f type for option '<arg:3>'.</yellow>"
|
||||
warning.config.type.map: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Map type for option '<arg:3>'.</yellow>"
|
||||
warning.config.type.snbt.invalid_syntax: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Invalid snbt syntax '<arg:2>'.</yellow>"
|
||||
warning.config.number.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for number argument.</yellow>"
|
||||
warning.config.number.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid number argument type '<arg:2>'.</yellow>"
|
||||
@@ -142,6 +143,8 @@ warning.config.recipe.smithing_trim.missing_addition: "<yellow>Issue found in fi
|
||||
warning.config.recipe.smithing_trim.missing_pattern: "<yellow>Issue found in file <arg:0> - The smithing trim recipe '<arg:1>' is missing the required 'pattern' argument.</yellow>"
|
||||
warning.config.recipe.brewing.missing_container: "<yellow>Issue found in file <arg:0> - The brewing recipe '<arg:1>' is missing the required 'container' argument.</yellow>"
|
||||
warning.config.recipe.brewing.missing_ingredient: "<yellow>Issue found in file <arg:0> - The brewing recipe '<arg:1>' is missing the required 'ingredient' argument.</yellow>"
|
||||
warning.config.recipe.result.post_processor.missing_type: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is missing the required 'type' argument for result post processors.</yellow>"
|
||||
warning.config.recipe.result.post_processor.invalid_type: "<yellow>Issue found in file <arg:0> - The recipe '<arg:1>' is using an invalid result post processor type '<arg:2>'.</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>"
|
||||
@@ -173,6 +176,9 @@ warning.config.item.data.attribute_modifiers.missing_amount: "<yellow>Issue foun
|
||||
warning.config.item.data.attribute_modifiers.missing_operation: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'operation' argument for 'attribute-modifiers' data.</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.display.missing_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'type' argument for 'attribute-modifiers' display data.</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.display.missing_value: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'value' argument for 'attribute-modifiers' display data.</yellow>"
|
||||
warning.config.item.data.external.missing_source: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'source' argument for 'external' data.</yellow>"
|
||||
warning.config.item.data.external.missing_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'id' argument for 'external' data.</yellow>"
|
||||
warning.config.item.data.external.invalid_source: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid item source '<arg:2>' for 'external' data.</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>"
|
||||
@@ -187,6 +193,7 @@ warning.config.item.behavior.invalid_type: "<yellow>Issue found in file <arg:0>
|
||||
warning.config.item.behavior.block.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'block_item' behavior.</yellow>"
|
||||
warning.config.item.behavior.furniture.missing_furniture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'furniture' argument for 'furniture_item' behavior.</yellow>"
|
||||
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'liquid_collision_block_item' behavior.</yellow>"
|
||||
warning.config.item.behavior.double_high.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'double_high_block_item' behavior.</yellow>"
|
||||
warning.config.item.legacy_model.missing_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the require 'path' argument for legacy-model.</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the require 'path' argument for legacy-model overrides.</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_predicate: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the require 'predicate' argument for legacy-model overrides.</yellow>"
|
||||
@@ -291,7 +298,7 @@ warning.config.block.behavior.stairs.missing_half: "<yellow>Issue found in file
|
||||
warning.config.block.behavior.stairs.missing_shape: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'shape' property for 'stairs_block' behavior.</yellow>"
|
||||
warning.config.block.behavior.pressure_plate.missing_powered: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'powered' property for 'pressure_plate_block' behavior.</yellow>"
|
||||
warning.config.block.behavior.grass.missing_feature: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'feature' argument for 'grass_block' behavior.</yellow>"
|
||||
warning.config.block.behavior.double.missing_half: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'half' property for 'double_block' behavior.</yellow>"
|
||||
warning.config.block.behavior.double_high.missing_half: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'half' property for 'double_block' behavior.</yellow>"
|
||||
warning.config.model.generation.missing_parent: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'parent' argument in 'generation' section.</yellow>"
|
||||
warning.config.model.generation.invalid_display_position: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid display position '<arg:2>' in 'generation.display' section. Allowed display positions: [<arg:3>]</yellow>"
|
||||
warning.config.model.generation.invalid_gui_light: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid gui-light option '<arg:2>' in 'generation' section. Allowed gui light options: [<arg:3>]</yellow>"
|
||||
@@ -393,6 +400,7 @@ warning.config.function.potion_effect.missing_potion_effect: "<yellow>Issue foun
|
||||
warning.config.function.set_cooldown.missing_time: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'time' argument for 'set_cooldown' function.</yellow>"
|
||||
warning.config.function.set_cooldown.missing_id: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'id' argument for 'set_cooldown' function.</yellow>"
|
||||
warning.config.function.remove_cooldown.missing_id: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'id' argument for 'remove_cooldown' function.</yellow>"
|
||||
warning.config.function.mythic_mobs_skill.missing_skill: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'skill' argument for 'mythic_mobs_skill' function.</yellow>"
|
||||
warning.config.selector.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for selector.</yellow>"
|
||||
warning.config.selector.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector type '<arg:2>'.</yellow>"
|
||||
warning.config.selector.invalid_target: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector target '<arg:2>'.</yellow>"
|
||||
|
||||
@@ -59,20 +59,21 @@ command.resource.enable.failure.unknown: "<red>未知资源 <arg:0></red>"
|
||||
command.resource.disable.success: "<white>已禁用 <arg:0>. 执行 <click:run_command:/ce reload all><u>/ce reload all</u></click> 以应用更改</white>"
|
||||
command.resource.disable.failure.unknown: "<red>未知资源 <arg:0></red>"
|
||||
command.resource.list: "<white>启用的资源(<arg:0>): <green><arg:1></green><newline>禁用的资源(<arg:2>): <red><arg:3></red></white>"
|
||||
command.upload.failure.not_supported: "<red>当前托管模式 '<arg:0>' 不支持上传资源包.</red>"
|
||||
command.upload.on_progress: "<white>已开始上传进程. 检查控制台以获取详细信息.</white>"
|
||||
command.upload.failure.not_supported: "<red>当前托管模式 '<arg:0>' 不支持上传资源包</red>"
|
||||
command.upload.on_progress: "<white>已开始上传进程. 检查控制台以获取详细信息</white>"
|
||||
command.send_resource_pack.success.single: "<white>发送资源包给 <arg:0></white>"
|
||||
command.send_resource_pack.success.multiple: "<white>发送资源包给 <arg:0> 个玩家</white>"
|
||||
warning.config.pack.duplicated_files: "</red>发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决</red>"
|
||||
warning.config.yaml.duplicated_key: "<red>在文件 <arg:0> 发现问题 - 在第<arg:2>行发现重复的键 '<arg:1>', 这可能会导致一些意料之外的问题.</red>"
|
||||
warning.config.yaml.inconsistent_value_type: "<red>在文件 <arg:0> 发现问题 - 在第<arg:2>行发现重复且值类型不同的键 '<arg:1>', 这可能会导致一些意料之外的问题.</red>"
|
||||
warning.config.yaml.duplicated_key: "<red>在文件 <arg:0> 发现问题 - 在第<arg:2>行发现重复的键 '<arg:1>', 这可能会导致一些意料之外的问题</red>"
|
||||
warning.config.yaml.inconsistent_value_type: "<red>在文件 <arg:0> 发现问题 - 在第<arg:2>行发现重复且值类型不同的键 '<arg:1>', 这可能会导致一些意料之外的问题</red>"
|
||||
warning.config.type.int: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为整数类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.float: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为浮点数类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.boolean: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为布尔类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.float: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为浮点数类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.double: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为双精度类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.quaternionf: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为四元数类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.vector3f: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为三维向量类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.snbt.invalid_syntax: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无效的 SNBT 语法 '<arg:2>'.</yellow>"
|
||||
warning.config.type.map: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为映射类型 (选项 '<arg:3>')</yellow>"
|
||||
warning.config.type.snbt.invalid_syntax: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无效的 SNBT 语法 '<arg:2>'</yellow>"
|
||||
warning.config.number.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少数字类型所需的 'type' 参数</yellow>"
|
||||
warning.config.number.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 使用了无效的数字类型 '<arg:2>'</yellow>"
|
||||
warning.config.number.missing_argument: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少数字参数</yellow>"
|
||||
@@ -102,11 +103,11 @@ warning.config.condition.string_contains.missing_value2: "<yellow>在文件 <arg
|
||||
warning.config.condition.string_regex.missing_value: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'string_regex' 条件必需的 'value' 参数</yellow>"
|
||||
warning.config.condition.string_regex.missing_regex: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'string_regex' 条件必需的 'regex' 参数</yellow>"
|
||||
warning.config.condition.expression.missing_expression: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'expression' 条件必需的 'expression' 参数</yellow>"
|
||||
warning.config.condition.is_null.missing_argument: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'is_null' 条件的必需的 'argument' 参数.</yellow>"
|
||||
warning.config.structure.not_section: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 应为配置段落 但实际类型为 '<arg:2>'</yellow>"
|
||||
warning.config.condition.is_null.missing_argument: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'is_null' 条件的必需的 'argument' 参数</yellow>"
|
||||
warning.config.condition.hand.missing_hand: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'hand' 条件必需的 'hand' 参数</yellow>"
|
||||
warning.config.condition.hand.invalid_hand: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 使用了无效的 'hand' 参数 '<arg:2>'('hand' 条件)。允许的手部类型: [<arg:3>]</yellow>"
|
||||
warning.config.condition.on_cooldown.missing_id: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'on_cooldown' 条件必需的 'id' 参数</yellow>"
|
||||
warning.config.structure.not_section: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 应为配置段落 但实际类型为 '<arg:2>'</yellow>"
|
||||
warning.config.image.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的图片配置 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
|
||||
warning.config.image.missing_height: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必需的 'height' 参数</yellow>"
|
||||
warning.config.image.height_ascent_conflict: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 违反位图规则: 'height' 参数 '<arg:2>' 必须不小于 'ascent' 参数 '<arg:3>'</yellow>"
|
||||
@@ -140,12 +141,16 @@ warning.config.recipe.smithing_trim.missing_base: "<yellow>在文件 <arg:0> 发
|
||||
warning.config.recipe.smithing_trim.missing_template_type: "<yellow>在文件 <arg:0> 发现问题 - 锻造纹饰配方 '<arg:1>' 缺少必需的 'template-type' 参数</yellow>"
|
||||
warning.config.recipe.smithing_trim.missing_addition: "<yellow>在文件 <arg:0> 发现问题 - 锻造纹饰配方 '<arg:1>' 缺少必需的 'addition' 参数</yellow>"
|
||||
warning.config.recipe.smithing_trim.missing_pattern: "<yellow>在文件 <arg:0> 发现问题 - 锻造纹饰配方 '<arg:1>' 缺少必需的 'pattern' 参数</yellow>"
|
||||
warning.config.recipe.brewing.missing_container: "<yellow>在文件 <arg:0> 发现问题 - 酿造配方 '<arg:1>' 缺少必需的 'container' 参数</yellow>"
|
||||
warning.config.recipe.brewing.missing_ingredient: "<yellow>在文件 <arg:0> 发现问题 - 酿造配方 '<arg:1>' 缺少必需的 'ingredient' 参数</yellow>"
|
||||
warning.config.recipe.result.post_processor.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 缺少结果后处理器必需的 'type' 参数</yellow>"
|
||||
warning.config.recipe.result.post_processor.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 使用了无效结果后处理器类型 '<arg:2>'</yellow>"
|
||||
warning.config.i18n.unknown_locale: "<yellow>在文件 <arg:0> 发现问题 - 未知的语言环境 '<arg:1>'</yellow>"
|
||||
warning.config.template.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的模板 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
|
||||
warning.config.template.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 使用了无效的模板 '<arg:2>'</yellow>"
|
||||
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 在 'self_increase_int' 参数中使用了一个起始值 '<arg:2>' 大于终止值 '<arg:3>'</yellow>"
|
||||
warning.config.template.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 使用了无效的模板 '<arg:2>'.</yellow>"
|
||||
warning.config.template.argument.list.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 的 'list' 参数需要列表类型 但输入参数类型为 '<arg:2>'</yellow>"
|
||||
warning.config.template.argument.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少了 '<arg:2>' 必要的模板参数值. 请使用 arguments 选项进行配置或为此参数设定默认值.</yellow>"
|
||||
warning.config.template.argument.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少了 '<arg:2>' 必要的模板参数值. 请使用 arguments 选项进行配置或为此参数设定默认值</yellow>"
|
||||
warning.config.vanilla_loot.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 缺少必需的 'type' 参数</yellow>"
|
||||
warning.config.vanilla_loot.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 使用了无效类型 '<arg:2>' 允许的类型: [<arg:3>]</yellow>"
|
||||
warning.config.vanilla_loot.block.invalid_target: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 中存在无效的方块目标 '<arg:2>'</yellow>"
|
||||
@@ -163,21 +168,24 @@ warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>在文件 <arg:0
|
||||
warning.config.item.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的物品 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
|
||||
warning.config.item.settings.unknown: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了未知的设置类型 '<arg:2>'</yellow>"
|
||||
warning.config.item.settings.invulnerable.invalid_damage_source: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 物品使用了未知的伤害来源类型 '<arg:2>' 允许的来源: [<arg:3>]</yellow>"
|
||||
warning.config.item.settings.equipment.missing_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'equipment' 设置所需的 'asset-id' 参数.</yellow>"
|
||||
warning.config.item.settings.equipment.invalid_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 为 'equipment' 设置配置了无效的 'asset-id'. 这可能是因为你没有创建装备配置或是错误地拼写了 asset-id.</yellow>"
|
||||
warning.config.item.settings.projectile.missing_item: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'projectile' 设置所需的 'item' 参数.</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'type' 参数.</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.missing_amount: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'amount' 参数.</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.missing_operation: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'operation' 参数.</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.display.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 显示数据所需的 'type' 参数。</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.display.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 显示数据所需的 'value' 参数。</yellow>"
|
||||
warning.config.item.settings.equipment.missing_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'equipment' 设置所需的 'asset-id' 参数</yellow>"
|
||||
warning.config.item.settings.equipment.invalid_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 为 'equipment' 设置配置了无效的 'asset-id'. 这可能是因为你没有创建装备配置或是错误地拼写了 asset-id</yellow>"
|
||||
warning.config.item.settings.projectile.missing_item: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'projectile' 设置所需的 'item' 参数</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'type' 参数</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.missing_amount: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'amount' 参数</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.missing_operation: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'operation' 参数</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.display.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 显示数据所需的 'type' 参数</yellow>"
|
||||
warning.config.item.data.attribute_modifiers.display.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 显示数据所需的 'value' 参数</yellow>"
|
||||
warning.config.item.data.external.missing_source: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'external' 数据所需的 'source' 参数</yellow>"
|
||||
warning.config.item.data.external.missing_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'external' 数据所需的 'id' 参数</yellow>"
|
||||
warning.config.item.data.external.invalid_source: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 在 'external' 数据中使用了无效的物品来源 '<arg:2>'</yellow>"
|
||||
warning.config.item.missing_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少必需的 'material' 参数</yellow>"
|
||||
warning.config.item.invalid_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的材料类型 '<arg:2>'</yellow>"
|
||||
warning.config.item.invalid_custom_model_data: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的负数模型值 '<arg:2>'.</yellow>"
|
||||
warning.config.item.invalid_custom_model_data: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的负数模型值 '<arg:2>'</yellow>"
|
||||
warning.config.item.bad_custom_model_data: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用的自定义模型数据 '<arg:2>' 数值过大 建议使用小于 16,777,216 的值</yellow>"
|
||||
warning.config.item.item_model.conflict: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的 'item-model' 选项. 这个 item-model 已经存在对应的原版物品</yellow>"
|
||||
warning.config.item.custom_model_data_conflict: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用的自定义模型数据 '<arg:2>' 已被物品 '<arg:3>' 占用</yellow>"
|
||||
warning.config.item.invalid_component: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了未知的数据组件 '<arg:2>'</yellow>"
|
||||
warning.config.item.item_model.conflict: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的 'item-model' 选项. 这个 item-model 已经存在对应的原版物品.</yellow>"
|
||||
warning.config.item.missing_model_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少必需的 'custom-model-data' 或 'item-model' 参数</yellow>"
|
||||
warning.config.item.missing_model: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 缺少支持 1.21.4+ 资源包必需的 'model' 配置项</yellow>"
|
||||
warning.config.item.behavior.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的行为配置缺少必需的 'type' 参数</yellow>"
|
||||
@@ -185,6 +193,7 @@ warning.config.item.behavior.invalid_type: "<yellow>在文件 <arg:0> 发现问
|
||||
warning.config.item.behavior.block.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'block_item' 行为缺少必需的 'block' 参数</yellow>"
|
||||
warning.config.item.behavior.furniture.missing_furniture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'furniture_item' 行为缺少必需的 'furniture' 参数</yellow>"
|
||||
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'liquid_collision_block_item' 行为缺少必需的 'block' 参数</yellow>"
|
||||
warning.config.item.behavior.double_high.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'double_high_block_item' 行为缺少必需的 'block' 参数</yellow>"
|
||||
warning.config.item.legacy_model.missing_path: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 的旧版模型(legacy-model)缺少必需的 'path' 参数</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_path: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 的旧版模型覆写规则(overrides)缺少必需的 'path' 参数</yellow>"
|
||||
warning.config.item.legacy_model.overrides.missing_predicate: "<yellow>在文件 <arg:0> 中发现问题 - 物品 '<arg:1>' 的旧版模型覆写规则(overrides)缺少必需的 'predicate' 参数</yellow>"
|
||||
@@ -204,8 +213,8 @@ warning.config.item.model.condition.missing_on_true: "<yellow>在文件 <arg:0>
|
||||
warning.config.item.model.condition.missing_on_false: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:condition' 模型缺少必需的 'on-false' 参数</yellow>"
|
||||
warning.config.item.model.condition.keybind.missing_keybind: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:keybind_down' 属性缺少必需的 'keybind' 参数</yellow>"
|
||||
warning.config.item.model.condition.has_component.missing_component: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:has_component' 属性缺少必需的 'component' 参数</yellow>"
|
||||
warning.config.item.model.condition.component.missing_predicate: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:component' 属性缺少必需的 'predicate' 参数.</yellow>"
|
||||
warning.config.item.model.condition.component.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:component' 属性缺少必需的 'value' 参数.</yellow>"
|
||||
warning.config.item.model.condition.component.missing_predicate: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:component' 属性缺少必需的 'predicate' 参数</yellow>"
|
||||
warning.config.item.model.condition.component.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:component' 属性缺少必需的 'value' 参数/yellow>"
|
||||
warning.config.item.model.composite.missing_models: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:composite' 模型缺少必需的 'models' 参数</yellow>"
|
||||
warning.config.item.model.range_dispatch.missing_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:range_dispatch' 模型缺少必需的 'property' 参数</yellow>"
|
||||
warning.config.item.model.range_dispatch.invalid_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:range_dispatch' 模型使用了无效属性 '<arg:2>'</yellow>"
|
||||
@@ -239,7 +248,7 @@ warning.config.block.missing_state: "<yellow>在文件 <arg:0> 发现问题 -
|
||||
warning.config.block.state.property.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的属性 '<arg:2>' 缺少必需的 'type' 参数</yellow>"
|
||||
warning.config.block.state.property.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的属性 '<arg:3>' 使用了无效的类型参数 '<arg:2>'</yellow>"
|
||||
warning.config.block.state.property.integer.invalid_range: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的整数属性 '<arg:3>' 使用了无效的范围参数 '<arg:2>' 正确语法: 1~2</yellow>"
|
||||
warning.config.block.state.property.invalid_format: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了无效的方块状态属性格式 '<arg:2>'.</yellow>"
|
||||
warning.config.block.state.property.invalid_format: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用了无效的方块状态属性格式 '<arg:2>'</yellow>"
|
||||
warning.config.block.state.missing_real_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'state' 缺少必需的 'id' 参数 该 ID 是服务端方块 ID 用于唯一标识每种方块状态类型</yellow>"
|
||||
warning.config.block.state.missing_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'state' 缺少必需的 'state' 参数</yellow>"
|
||||
warning.config.block.state.missing_properties: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'states' 缺少必需的 'properties' 段落</yellow>"
|
||||
@@ -289,7 +298,7 @@ warning.config.block.behavior.stairs.missing_half: "<yellow>在文件 <arg:0>
|
||||
warning.config.block.behavior.stairs.missing_shape: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'stairs_block' 行为缺少必需的 'shape' 属性</yellow>"
|
||||
warning.config.block.behavior.pressure_plate.missing_powered: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'pressure_plate_block' 行为缺少必需的 'powered' 属性</yellow>"
|
||||
warning.config.block.behavior.grass.missing_feature: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'grass_block' 行为缺少必需的 'feature' 参数</yellow>"
|
||||
warning.config.block.behavior.double.missing_half: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'double_block' 行为缺少必需的 'half' 属性</yellow>"
|
||||
warning.config.block.behavior.double_high.missing_half: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'double_block' 行为缺少必需的 'half' 属性</yellow>"
|
||||
warning.config.model.generation.missing_parent: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 'generation' 段落缺少必需的 'parent' 参数</yellow>"
|
||||
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 发现问题 - 无法为 '<arg:1>' 生成模型 存在多个配置尝试使用相同路径 '<arg:2>' 生成不同的 JSON 模型</yellow>"
|
||||
warning.config.model.generation.invalid_display_position: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 在 'generation.display' 区域使用了无效的 display 位置类型 '<arg:2>'. 可用展示类型: [<arg:3>]</yellow>"
|
||||
@@ -371,9 +380,9 @@ warning.config.function.command.missing_command: "<yellow>在文件 <arg:0> 中
|
||||
warning.config.function.actionbar.missing_actionbar: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'actionbar' 函数必需的 'actionbar' 参数</yellow>"
|
||||
warning.config.function.message.missing_message: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'message' 函数必需的 'message' 参数</yellow>"
|
||||
warning.config.function.open_window.missing_gui_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'open_window' 函数必需的 'gui-type' 参数</yellow>"
|
||||
warning.config.function.open_window.invalid_gui_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 为 'open_window' 函数使用了无效的 GUI 类型 <arg:2>. 允许的类型: [<arg:3>]。</yellow>"
|
||||
warning.config.function.open_window.invalid_gui_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 为 'open_window' 函数使用了无效的 GUI 类型 <arg:2>. 允许的类型: [<arg:3>]</yellow>"
|
||||
warning.config.function.run.missing_functions: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'run' 函数必需的 'functions' 参数</yellow>"
|
||||
warning.config.function.place_block.missing_block_state: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'place_block' 函数必需的 'block-state' 参数.</yellow>"
|
||||
warning.config.function.place_block.missing_block_state: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'place_block' 函数必需的 'block-state' 参数</yellow>"
|
||||
warning.config.function.set_food.missing_food: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'set_food' 函数必需的 'food' 参数</yellow>"
|
||||
warning.config.function.set_saturation.missing_saturation: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'set_saturation' 函数必需的 'saturation' 参数</yellow>"
|
||||
warning.config.function.play_sound.missing_sound: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'play_sound' 函数必需的 'sound' 参数</yellow>"
|
||||
@@ -391,6 +400,7 @@ warning.config.function.potion_effect.missing_potion_effect: "<yellow>在文件
|
||||
warning.config.function.set_cooldown.missing_time: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'set_cooldown' 函数必需的 'time' 参数</yellow>"
|
||||
warning.config.function.set_cooldown.missing_id: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'set_cooldown' 函数必需的 'id' 参数</yellow>"
|
||||
warning.config.function.remove_cooldown.missing_id: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'remove_cooldown' 函数必需的 'id' 参数</yellow>"
|
||||
warning.config.function.mythic_mobs_skill.missing_skill: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'mythic_mobs_skill' 函数必需的 'skill' 参数</yellow>"
|
||||
warning.config.selector.missing_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少选择器必需的 'type' 参数</yellow>"
|
||||
warning.config.selector.invalid_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 使用了无效的选择器类型 '<arg:2>'</yellow>"
|
||||
warning.config.selector.invalid_target: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 使用了无效的选择器目标 '<arg:2>'</yellow>"
|
||||
@@ -402,9 +412,9 @@ warning.config.resource_pack.generation.texture_not_in_atlas: "<yellow>纹理'<a
|
||||
warning.config.resource_pack.generation.missing_item_model: "<yellow>物品'<arg:0>'缺少模型文件: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.missing_block_model: "<yellow>方块状态'<arg:0>'缺少模型文件: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.missing_parent_model: "<yellow>模型'<arg:0>'找不到父级模型文件: '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.generation.malformatted_json: "<yellow>Json文件 '<arg:0>' 格式错误.</yellow>"
|
||||
warning.config.resource_pack.generation.malformatted_json: "<yellow>Json文件 '<arg:0>' 格式错误</yellow>"
|
||||
warning.config.resource_pack.generation.missing_equipment_texture: "<yellow>装备 '<arg:0>' 缺少纹理 '<arg:1>'</yellow>"
|
||||
warning.config.resource_pack.invalid_overlay_format: "<yellow>在 config.yml 的 'resource-pack.overlay-format' 处发现问题 - 无效的overlay格式 '<arg:0>'. Overlay格式必须包含占位符 '{version}'.</yellow>"
|
||||
warning.config.resource_pack.invalid_overlay_format: "<yellow>在 config.yml 的 'resource-pack.overlay-format' 处发现问题 - 无效的overlay格式 '<arg:0>'. Overlay格式必须包含占位符 '{version}'</yellow>"
|
||||
warning.config.equipment.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的装备配置 '<arg:1>'。请检查其他文件中是否存在相同配置</yellow>"
|
||||
warning.config.equipment.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 装备 '<arg:1>' 缺少必需的 'type' 参数</yellow>"
|
||||
warning.config.equipment.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 装备 '<arg:1>' 使用了无效的 'type' 参数</yellow>"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-beta13"
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.attribute;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -72,7 +73,7 @@ public class AttributeModifier {
|
||||
}
|
||||
}
|
||||
|
||||
public record Display(AttributeModifier.Display.Type type, String value) {
|
||||
public record Display(AttributeModifier.Display.Type type, Component value) {
|
||||
|
||||
public enum Type {
|
||||
DEFAULT, HIDDEN, OVERRIDE
|
||||
|
||||
@@ -41,7 +41,25 @@ public final class BlockKeys {
|
||||
public static final Key COMMAND_BLOCK = Key.of("minecraft:command_block");
|
||||
public static final Key CHAIN_COMMAND_BLOCK = Key.of("minecraft:chain_command_block");
|
||||
public static final Key REPEATING_COMMAND_BLOCK = Key.of("minecraft:repeating_command_block");
|
||||
public static final Key JIGSAW = Key.of("minecraft:jigsaw");
|
||||
public static final Key STRUCTURE_BLOCK = Key.of("minecraft:structure_block");
|
||||
public static final Key TEST_INSTANCE_BLOCK = Key.of("minecraft:test_instance_block");
|
||||
public static final Key TEST_BLOCK = Key.of("minecraft:test_block");
|
||||
public static final Key LIGHT = Key.of("minecraft:light");
|
||||
public static final Key DECORATED_POT = Key.of("minecraft:decorated_pot");
|
||||
public static final Key FLOWER_POT = Key.of("minecraft:flower_pot");
|
||||
public static final Key CHISELED_BOOKSHELF = Key.of("minecraft:chiseled_bookshelf");
|
||||
public static final Key REDSTONE_ORE = Key.of("minecraft:redstone_ore");
|
||||
public static final Key DEEPSLATE_REDSTONE_ORE = Key.of("minecraft:deepslate_redstone_ore");
|
||||
public static final Key BEE_NEST = Key.of("minecraft:bee_nest");
|
||||
public static final Key BEEHIVE = Key.of("minecraft:beehive");
|
||||
public static final Key POWDER_SNOW = Key.of("minecraft:powder_snow");
|
||||
public static final Key COMPOSTER = Key.of("minecraft:composter");
|
||||
public static final Key CAULDRON = Key.of("minecraft:cauldron");
|
||||
public static final Key WATER_CAULDRON = Key.of("minecraft:water_cauldron");
|
||||
public static final Key LAVA_CAULDRON = Key.of("minecraft:lava_cauldron");
|
||||
public static final Key RESPAWN_ANCHOR = Key.of("minecraft:respawn_anchor");
|
||||
public static final Key LODESTONE = Key.of("minecraft:lodestone");
|
||||
|
||||
public static final Key CAKE = Key.of("minecraft:cake");
|
||||
public static final Key CANDLE_CAKE = Key.of("minecraft:candle_cake");
|
||||
@@ -158,6 +176,8 @@ public final class BlockKeys {
|
||||
public static final Key WAXED_OXIDIZED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_oxidized_copper_trapdoor");
|
||||
public static final Key WAXED_WEATHERED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_weathered_copper_trapdoor");
|
||||
|
||||
|
||||
|
||||
public static final Key OAK_FENCE_GATE = Key.of("minecraft:oak_fence_gate");
|
||||
public static final Key SPRUCE_FENCE_GATE = Key.of("minecraft:spruce_fence_gate");
|
||||
public static final Key BIRCH_FENCE_GATE = Key.of("minecraft:birch_fence_gate");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.block;
|
||||
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -34,7 +35,7 @@ public class BlockSettings {
|
||||
Key itemId;
|
||||
Set<Key> tags = Set.of();
|
||||
float incorrectToolSpeed = 0.3f;
|
||||
Set<Key> correctTools = Set.of();
|
||||
LazyReference<Set<Key>> correctTools = LazyReference.lazyReference(Set::of);
|
||||
String name;
|
||||
String supportShapeBlockState;
|
||||
|
||||
@@ -148,7 +149,7 @@ public class BlockSettings {
|
||||
}
|
||||
|
||||
public boolean requireCorrectTool() {
|
||||
return requireCorrectTools || !correctTools.isEmpty();
|
||||
return requireCorrectTools || !correctTools.get().isEmpty();
|
||||
}
|
||||
|
||||
public String name() {
|
||||
@@ -196,7 +197,7 @@ public class BlockSettings {
|
||||
}
|
||||
|
||||
public boolean isCorrectTool(Key key) {
|
||||
return this.correctTools.contains(key);
|
||||
return this.correctTools.get().contains(key);
|
||||
}
|
||||
|
||||
public boolean respectToolComponent() {
|
||||
@@ -215,7 +216,7 @@ public class BlockSettings {
|
||||
return useShapeForLightOcclusion;
|
||||
}
|
||||
|
||||
public BlockSettings correctTools(Set<Key> correctTools) {
|
||||
public BlockSettings correctTools(LazyReference<Set<Key>> correctTools) {
|
||||
this.correctTools = correctTools;
|
||||
return this;
|
||||
}
|
||||
@@ -417,7 +418,10 @@ public class BlockSettings {
|
||||
}));
|
||||
registerFactory("tags", (value -> {
|
||||
List<String> tags = MiscUtils.getAsStringList(value);
|
||||
return settings -> settings.tags(tags.stream().map(Key::of).collect(Collectors.toSet()));
|
||||
return settings -> settings.tags(tags.stream().map(it -> {
|
||||
if (it.charAt(0) == '#') return Key.of(it.substring(1));
|
||||
else return Key.of(it);
|
||||
}).collect(Collectors.toSet()));
|
||||
}));
|
||||
registerFactory("burn-chance", (value -> {
|
||||
int intValue = ResourceConfigUtils.getAsInt(value, "burn-chance");
|
||||
@@ -457,7 +461,15 @@ public class BlockSettings {
|
||||
}));
|
||||
registerFactory("correct-tools", (value -> {
|
||||
List<String> tools = MiscUtils.getAsStringList(value);
|
||||
return settings -> settings.correctTools(tools.stream().map(Key::of).collect(Collectors.toSet()));
|
||||
LazyReference<Set<Key>> correctTools = LazyReference.lazyReference(() -> {
|
||||
Set<Key> ids = new HashSet<>();
|
||||
for (String tool : tools) {
|
||||
if (tool.charAt(0) == '#') ids.addAll(CraftEngine.instance().itemManager().itemIdsByTag(Key.of(tool.substring(1))).stream().map(UniqueKey::key).toList());
|
||||
else ids.add(Key.of(tool));
|
||||
}
|
||||
return ids;
|
||||
});
|
||||
return settings -> settings.correctTools(correctTools);
|
||||
}));
|
||||
registerFactory("require-correct-tools", (value -> {
|
||||
boolean booleanValue = ResourceConfigUtils.getAsBoolean(value, "require-correct-tools");
|
||||
|
||||
@@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class Properties {
|
||||
public final class Properties {
|
||||
public static final Key BOOLEAN = Key.of("craftengine:boolean");
|
||||
public static final Key INT = Key.of("craftengine:int");
|
||||
public static final Key STRING = Key.of("craftengine:string");
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package net.momirealms.craftengine.core.entity;
|
||||
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public class EntityTypeKeys {
|
||||
private EntityTypeKeys() {}
|
||||
|
||||
public static final Key BEE = Key.of("minecraft:bee");
|
||||
public static final Key FOX = Key.of("minecraft:fox");
|
||||
public static final Key FROG = Key.of("minecraft:frog");
|
||||
public static final Key PANDA = Key.of("minecraft:panda");
|
||||
public static final Key SHEEP = Key.of("minecraft:sheep");
|
||||
public static final Key BOGGED = Key.of("minecraft:bogged");
|
||||
public static final Key SNOW_GOLEM = Key.of("minecraft:snow_golem");
|
||||
public static final Key HOGLIN = Key.of("minecraft:hoglin");
|
||||
public static final Key OCELOT = Key.of("minecraft:ocelot");
|
||||
public static final Key RABBIT = Key.of("minecraft:rabbit");
|
||||
public static final Key TURTLE = Key.of("minecraft:turtle");
|
||||
public static final Key AXOLOTL = Key.of("minecraft:axolotl");
|
||||
public static final Key CHICKEN = Key.of("minecraft:chicken");
|
||||
public static final Key SNIFFER = Key.of("minecraft:sniffer");
|
||||
public static final Key ARMADILLO = Key.of("minecraft:armadillo");
|
||||
public static final Key COD = Key.of("minecraft:cod");
|
||||
public static final Key SALMON = Key.of("minecraft:salmon");
|
||||
public static final Key TROPICAL_FISH = Key.of("minecraft:tropical_fish");
|
||||
public static final Key PUFFERFISH = Key.of("minecraft:pufferfish");
|
||||
public static final Key TADPOLE = Key.of("minecraft:tadpole");
|
||||
public static final Key COW = Key.of("minecraft:cow");
|
||||
public static final Key MOOSHROOM = Key.of("minecraft:mooshroom");
|
||||
public static final Key GOAT = Key.of("minecraft:goat");
|
||||
public static final Key PIG = Key.of("minecraft:pig");
|
||||
public static final Key STRIDER = Key.of("minecraft:strider");
|
||||
public static final Key WOLF = Key.of("minecraft:wolf");
|
||||
public static final Key CAT = Key.of("minecraft:cat");
|
||||
public static final Key PARROT = Key.of("minecraft:parrot");
|
||||
|
||||
public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast");
|
||||
public static final Key PIGLIN = Key.of("minecraft:piglin");
|
||||
public static final Key CREEPER = Key.of("minecraft:creeper");
|
||||
public static final Key ALLAY = Key.of("minecraft:allay");
|
||||
public static final Key HORSE = Key.of("minecraft:horse");
|
||||
public static final Key ZOMBIE_HORSE = Key.of("minecraft:zombie_horse");
|
||||
public static final Key SKELETON_HORSE = Key.of("minecraft:skeleton_horse");
|
||||
public static final Key DONKEY = Key.of("minecraft:donkey");
|
||||
public static final Key MULE = Key.of("minecraft:mule");
|
||||
public static final Key VILLAGER = Key.of("minecraft:villager");
|
||||
public static final Key WANDERING_TRADER = Key.of("minecraft:wandering_trader");
|
||||
public static final Key LLAMA = Key.of("minecraft:llama");
|
||||
public static final Key TRADER_LLAMA = Key.of("minecraft:trader_llama");
|
||||
public static final Key CAMEL = Key.of("minecraft:camel");
|
||||
public static final Key ITEM_FRAME = Key.of("minecraft:item_frame");
|
||||
public static final Key GLOW_ITEM_FRAME = Key.of("minecraft:glow_item_frame");
|
||||
public static final Key INTERACTION = Key.of("minecraft:interaction");
|
||||
|
||||
public static final Key BOAT = Key.of("minecraft:boat");
|
||||
public static final Key OAK_BOAT = Key.of("minecraft:oak_boat");
|
||||
public static final Key SPRUCE_BOAT = Key.of("minecraft:spruce_boat");
|
||||
public static final Key BIRCH_BOAT = Key.of("minecraft:birch_boat");
|
||||
public static final Key JUNGLE_BOAT = Key.of("minecraft:jungle_boat");
|
||||
public static final Key ACACIA_BOAT = Key.of("minecraft:acacia_boat");
|
||||
public static final Key DARK_OAK_BOAT = Key.of("minecraft:dark_oak_boat");
|
||||
public static final Key MANGROVE_BOAT = Key.of("minecraft:mangrove_boat");
|
||||
public static final Key CHERRY_BOAT = Key.of("minecraft:cherry_boat");
|
||||
public static final Key PALE_OAK_BOAT = Key.of("minecraft:pale_oak_boat");
|
||||
public static final Key BAMBOO_RAFT = Key.of("minecraft:bamboo_raft");
|
||||
|
||||
public static final Key CHEST_BOAT = Key.of("minecraft:chest_boat");
|
||||
public static final Key OAK_CHEST_BOAT = Key.of("minecraft:oak_chest_boat");
|
||||
public static final Key SPRUCE_CHEST_BOAT = Key.of("minecraft:spruce_chest_boat");
|
||||
public static final Key BIRCH_CHEST_BOAT = Key.of("minecraft:birch_chest_boat");
|
||||
public static final Key JUNGLE_CHEST_BOAT = Key.of("minecraft:jungle_chest_boat");
|
||||
public static final Key ACACIA_CHEST_BOAT = Key.of("minecraft:acacia_chest_boat");
|
||||
public static final Key DARK_OAK_CHEST_BOAT = Key.of("minecraft:dark_oak_chest_boat");
|
||||
public static final Key MANGROVE_CHEST_BOAT = Key.of("minecraft:mangrove_chest_boat");
|
||||
public static final Key CHERRY_CHEST_BOAT = Key.of("minecraft:cherry_chest_boat");
|
||||
public static final Key PALE_OAK_CHEST_BOAT = Key.of("minecraft:pale_oak_chest_boat");
|
||||
public static final Key BAMBOO_CHEST_RAFT = Key.of("minecraft:bamboo_chest_raft");
|
||||
|
||||
public static final Key MINECART = Key.of("minecraft:minecart");
|
||||
public static final Key CHEST_MINECART = Key.of("minecraft:chest_minecart");
|
||||
public static final Key FURNACE_MINECART = Key.of("minecraft:furnace_minecart");
|
||||
public static final Key HOPPER_MINECART = Key.of("minecraft:hopper_minecart");
|
||||
public static final Key COMMAND_BLOCK_MINECART = Key.of("minecraft:command_block_minecart");
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.entity.furniture;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||
import net.momirealms.craftengine.core.util.Color;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import net.momirealms.sparrow.nbt.NBT;
|
||||
|
||||
@@ -49,8 +50,8 @@ public class FurnitureExtraData {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Optional<Integer> dyedColor() {
|
||||
if (this.data.containsKey(DYED_COLOR)) return Optional.of(this.data.getInt(DYED_COLOR));
|
||||
public Optional<Color> dyedColor() {
|
||||
if (this.data.containsKey(DYED_COLOR)) return Optional.of(Color.fromDecimal(this.data.getInt(DYED_COLOR)));
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -92,9 +93,9 @@ public class FurnitureExtraData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dyedColor(Integer color) {
|
||||
public Builder dyedColor(Color color) {
|
||||
if (color == null) return this;
|
||||
this.data.putInt(DYED_COLOR, color);
|
||||
this.data.putInt(DYED_COLOR, color.color());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
|
||||
|
||||
public abstract void sendPackets(List<Object> packet, boolean immediately);
|
||||
|
||||
public abstract void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener);
|
||||
|
||||
public abstract float getDestroyProgress(Object blockState, BlockPos pos);
|
||||
|
||||
public abstract void setClientSideCanBreakBlock(boolean canBreak);
|
||||
@@ -68,6 +70,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
|
||||
|
||||
public abstract int lastSuccessfulInteractionTick();
|
||||
|
||||
public abstract void updateLastInteractEntityTick(@NotNull InteractionHand hand);
|
||||
|
||||
public abstract boolean lastInteractEntityCheck(@NotNull InteractionHand hand);
|
||||
|
||||
public abstract int gameTicks();
|
||||
|
||||
public abstract void swingHand(InteractionHand hand);
|
||||
|
||||
@@ -368,7 +368,13 @@ public abstract class AbstractFontManager implements FontManager {
|
||||
if (keywords.isEmpty()) {
|
||||
throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id);
|
||||
}
|
||||
String content = section.getOrDefault("content", "<arg:emoji>").toString();
|
||||
Object rawContent = section.getOrDefault("content", "<white><arg:emoji></white>");
|
||||
String content;
|
||||
if (rawContent instanceof List<?> list) {
|
||||
content = list.stream().map(Object::toString).collect(Collectors.joining());
|
||||
} else {
|
||||
content = rawContent.toString();
|
||||
}
|
||||
String image = null;
|
||||
if (section.containsKey("image")) {
|
||||
String rawImage = section.get("image").toString();
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class AbstractCustomItem<I> implements CustomItem<I> {
|
||||
protected final boolean isVanillaItem;
|
||||
protected final UniqueKey id;
|
||||
protected final Key material;
|
||||
protected final Key clientBoundMaterial;
|
||||
@@ -25,12 +26,13 @@ public abstract class AbstractCustomItem<I> implements CustomItem<I> {
|
||||
protected final Map<EventTrigger, List<Function<PlayerOptionalContext>>> events;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public AbstractCustomItem(UniqueKey id, Key material, Key clientBoundMaterial,
|
||||
public AbstractCustomItem(boolean isVanillaItem, UniqueKey id, Key material, Key clientBoundMaterial,
|
||||
List<ItemBehavior> behaviors,
|
||||
List<ItemDataModifier<I>> modifiers,
|
||||
List<ItemDataModifier<I>> clientBoundModifiers,
|
||||
ItemSettings settings,
|
||||
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
|
||||
this.isVanillaItem = isVanillaItem;
|
||||
this.id = id;
|
||||
this.material = material;
|
||||
this.clientBoundMaterial = clientBoundMaterial;
|
||||
@@ -75,6 +77,11 @@ public abstract class AbstractCustomItem<I> implements CustomItem<I> {
|
||||
return this.modifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaItem() {
|
||||
return isVanillaItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasClientBoundDataModifier() {
|
||||
return this.clientBoundModifiers.length != 0;
|
||||
|
||||
@@ -2,15 +2,19 @@ package net.momirealms.craftengine.core.item;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.core.attribute.AttributeModifier;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.data.Enchantment;
|
||||
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||
import net.momirealms.craftengine.core.item.data.JukeboxPlayable;
|
||||
import net.momirealms.craftengine.core.item.data.Trim;
|
||||
import net.momirealms.craftengine.core.item.setting.EquipmentData;
|
||||
import net.momirealms.craftengine.core.util.Color;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.UniqueKey;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -107,13 +111,13 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<I> dyedColor(Integer data) {
|
||||
public Item<I> dyedColor(Color data) {
|
||||
this.factory.dyedColor(this.item, data);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> dyedColor() {
|
||||
public Optional<Color> dyedColor() {
|
||||
return this.factory.dyedColor(this.item);
|
||||
}
|
||||
|
||||
@@ -150,17 +154,17 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key id() {
|
||||
public @NotNull Key id() {
|
||||
return this.factory.id(this.item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key vanillaId() {
|
||||
public @NotNull Key vanillaId() {
|
||||
return this.factory.vanillaId(this.item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueKey recipeIngredientId() {
|
||||
public @Nullable UniqueKey recipeIngredientId() {
|
||||
return this.factory.recipeIngredientID(this.item);
|
||||
}
|
||||
|
||||
@@ -252,6 +256,12 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
|
||||
return this.factory.loreComponent(this.item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<I> attributeModifiers(List<AttributeModifier> modifiers) {
|
||||
this.factory.attributeModifiers(this.item, modifiers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<I> unbreakable(boolean unbreakable) {
|
||||
this.factory.unbreakable(this.item, unbreakable);
|
||||
@@ -433,8 +443,8 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is(Key itemTag) {
|
||||
return this.factory.is(this.item, itemTag);
|
||||
public boolean hasItemTag(Key itemTag) {
|
||||
return this.factory.hasItemTag(this.item, itemTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,14 +2,10 @@ package net.momirealms.craftengine.core.item;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.momirealms.craftengine.core.attribute.AttributeModifier;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehaviors;
|
||||
import net.momirealms.craftengine.core.item.data.Enchantment;
|
||||
import net.momirealms.craftengine.core.item.data.JukeboxPlayable;
|
||||
import net.momirealms.craftengine.core.item.equipment.*;
|
||||
import net.momirealms.craftengine.core.item.modifier.*;
|
||||
import net.momirealms.craftengine.core.item.setting.EquipmentData;
|
||||
import net.momirealms.craftengine.core.pack.AbstractPackManager;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
@@ -22,20 +18,18 @@ import net.momirealms.craftengine.core.pack.model.select.TrimMaterialSelectPrope
|
||||
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.PlayerOptionalContext;
|
||||
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
|
||||
import net.momirealms.craftengine.core.plugin.context.text.TextProvider;
|
||||
import net.momirealms.craftengine.core.plugin.context.text.TextProviders;
|
||||
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.type.Either;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class AbstractItemManager<I> extends AbstractModelGenerator implements ItemManager<I> {
|
||||
@@ -45,8 +39,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
|
||||
private final ItemParser itemParser;
|
||||
private final EquipmentParser equipmentParser;
|
||||
protected final Map<String, ExternalItemProvider<I>> externalItemProviders = new HashMap<>();
|
||||
protected final Map<String, Function<Object, ItemDataModifier<I>>> dataFunctions = new HashMap<>();
|
||||
protected final Map<String, ExternalItemSource<I>> externalItemSources = new HashMap<>();
|
||||
protected final Map<Key, CustomItem<I>> customItems = new HashMap<>();
|
||||
protected final Map<Key, List<UniqueKey>> customItemTags = new HashMap<>();
|
||||
protected final Map<Key, Map<Integer, Key>> cmdConflictChecker = new HashMap<>();
|
||||
@@ -63,14 +56,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
super(plugin);
|
||||
this.itemParser = new ItemParser();
|
||||
this.equipmentParser = new EquipmentParser();
|
||||
this.registerFunctions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDataType(Function<Object, ItemDataModifier<I>> factory, String... alias) {
|
||||
for (String a : alias) {
|
||||
this.dataFunctions.put(a, factory);
|
||||
}
|
||||
ItemDataModifiers.init();
|
||||
}
|
||||
|
||||
protected static void registerVanillaItemExtraBehavior(ItemBehavior behavior, Key... items) {
|
||||
@@ -79,18 +65,23 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyDataFunctions(Map<String, Object> dataSection, Consumer<ItemDataModifier<I>> consumer) {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void applyDataModifiers(Map<String, Object> dataSection, Consumer<ItemDataModifier<I>> callback) {
|
||||
ExceptionCollector<LocalizedResourceConfigException> errorCollector = new ExceptionCollector<>();
|
||||
if (dataSection != null) {
|
||||
for (Map.Entry<String, Object> dataEntry : dataSection.entrySet()) {
|
||||
Optional.ofNullable(this.dataFunctions.get(dataEntry.getKey())).ifPresent(function -> {
|
||||
Object value = dataEntry.getValue();
|
||||
if (value == null) continue;
|
||||
Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(dataEntry.getKey(), Key.DEFAULT_NAMESPACE))).ifPresent(factory -> {
|
||||
try {
|
||||
consumer.accept(function.apply(dataEntry.getValue()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
this.plugin.logger().warn("Invalid data format", e);
|
||||
callback.accept((ItemDataModifier<I>) factory.create(value));
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
errorCollector.add(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
errorCollector.throwIfPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,14 +90,15 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalItemProvider<I> getExternalItemProvider(String name) {
|
||||
return this.externalItemProviders.get(name);
|
||||
public ExternalItemSource<I> getExternalItemSource(String name) {
|
||||
return this.externalItemSources.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerExternalItemProvider(ExternalItemProvider<I> externalItemProvider) {
|
||||
if (this.externalItemProviders.containsKey(externalItemProvider.plugin())) return false;
|
||||
this.externalItemProviders.put(externalItemProvider.plugin(), externalItemProvider);
|
||||
public boolean registerExternalItemSource(ExternalItemSource<I> externalItemSource) {
|
||||
if (!ResourceLocation.isValidNamespace(externalItemSource.plugin())) return false;
|
||||
if (this.externalItemSources.containsKey(externalItemSource.plugin())) return false;
|
||||
this.externalItemSources.put(externalItemSource.plugin(), externalItemSource);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,43 +137,31 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
Key id = customItem.id();
|
||||
if (this.customItems.containsKey(id)) return false;
|
||||
this.customItems.put(id, customItem);
|
||||
// cache command suggestions
|
||||
this.cachedSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
// totem animations
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
} else if (customItem.material().equals(ItemKeys.TOTEM_OF_UNDYING)) {
|
||||
this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
}
|
||||
// tags
|
||||
Set<Key> tags = customItem.settings().tags();
|
||||
for (Key tag : tags) {
|
||||
this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId());
|
||||
if (!customItem.isVanillaItem()) {
|
||||
// cache command suggestions
|
||||
this.cachedSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
// totem animations
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
} else if (customItem.material().equals(ItemKeys.TOTEM_OF_UNDYING)) {
|
||||
this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString()));
|
||||
}
|
||||
// tags
|
||||
Set<Key> tags = customItem.settings().tags();
|
||||
for (Key tag : tags) {
|
||||
this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UniqueKey> tagToItems(Key tag) {
|
||||
List<UniqueKey> items = new ArrayList<>();
|
||||
List<UniqueKey> holders = VANILLA_ITEM_TAGS.get(tag);
|
||||
if (holders != null) {
|
||||
items.addAll(holders);
|
||||
}
|
||||
List<UniqueKey> customItems = this.customItemTags.get(tag);
|
||||
if (customItems != null) {
|
||||
items.addAll(customItems);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UniqueKey> tagToVanillaItems(Key tag) {
|
||||
public List<UniqueKey> vanillaItemIdsByTag(Key tag) {
|
||||
return Collections.unmodifiableList(VANILLA_ITEM_TAGS.getOrDefault(tag, List.of()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UniqueKey> tagToCustomItems(Key tag) {
|
||||
public List<UniqueKey> customItemIdsByTag(Key tag) {
|
||||
return Collections.unmodifiableList(this.customItemTags.getOrDefault(tag, List.of()));
|
||||
}
|
||||
|
||||
@@ -372,23 +352,63 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
else itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey));
|
||||
}
|
||||
|
||||
// 对于不重要的配置,可以仅警告,不返回
|
||||
ExceptionCollector<LocalizedResourceConfigException> collector = new ExceptionCollector<>();
|
||||
|
||||
// 应用物品数据
|
||||
applyDataFunctions(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier);
|
||||
applyDataFunctions(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier);
|
||||
try {
|
||||
applyDataModifiers(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier);
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
collector.add(e);
|
||||
}
|
||||
try {
|
||||
applyDataModifiers(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier);
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
collector.add(e);
|
||||
}
|
||||
|
||||
// 如果不是原版物品,那么加入ce的标识符
|
||||
if (!isVanillaItem)
|
||||
itemBuilder.dataModifier(new IdModifier<>(id));
|
||||
|
||||
// 事件
|
||||
Map<EventTrigger, List<net.momirealms.craftengine.core.plugin.context.function.Function<PlayerOptionalContext>>> eventTriggerListMap;
|
||||
try {
|
||||
eventTriggerListMap = EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"));
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
collector.add(e);
|
||||
eventTriggerListMap = Map.of();
|
||||
}
|
||||
|
||||
// 设置
|
||||
ItemSettings settings;
|
||||
try {
|
||||
settings = Optional.ofNullable(ResourceConfigUtils.get(section, "settings"))
|
||||
.map(map -> ItemSettings.fromMap(MiscUtils.castToMap(map, true)))
|
||||
.map(it -> isVanillaItem ? it.canPlaceRelatedVanillaBlock(true) : it)
|
||||
.orElse(ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem));
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
collector.add(e);
|
||||
settings = ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem);
|
||||
}
|
||||
|
||||
// 行为
|
||||
List<ItemBehavior> behaviors;
|
||||
try {
|
||||
behaviors = ItemBehaviors.fromObj(pack, path, id, ResourceConfigUtils.get(section, "behavior", "behaviors"));
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
collector.add(e);
|
||||
behaviors = Collections.emptyList();
|
||||
}
|
||||
|
||||
// 构建自定义物品
|
||||
CustomItem<I> customItem = itemBuilder
|
||||
.behaviors(ItemBehaviors.fromObj(pack, path, id, ResourceConfigUtils.get(section, "behavior", "behaviors")))
|
||||
.settings(Optional.ofNullable(ResourceConfigUtils.get(section, "settings"))
|
||||
.map(map -> ItemSettings.fromMap(MiscUtils.castToMap(map, true)))
|
||||
.map(it -> isVanillaItem ? it.canPlaceRelatedVanillaBlock(true) : it)
|
||||
.orElse(ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem)))
|
||||
.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")))
|
||||
.isVanillaItem(isVanillaItem)
|
||||
.behaviors(behaviors)
|
||||
.settings(settings)
|
||||
.events(eventTriggerListMap)
|
||||
.build();
|
||||
|
||||
// 添加到缓存
|
||||
addCustomItem(customItem);
|
||||
|
||||
@@ -401,6 +421,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
Map<String, Object> modelSection = MiscUtils.castToMap(section.get("model"), true);
|
||||
Map<String, Object> legacyModelSection = MiscUtils.castToMap(section.get("legacy-model"), true);
|
||||
if (modelSection == null && legacyModelSection == null) {
|
||||
collector.throwIfPresent();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -409,39 +430,47 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
if (!isVanillaItem) {
|
||||
// 既没有模型值也没有item-model
|
||||
if (customModelData == 0 && itemModelKey == null) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.missing_model_id");
|
||||
collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model_id"));
|
||||
}
|
||||
}
|
||||
|
||||
// 1.21.4+必须要配置model区域,如果不需要高版本兼容,则可以只写legacy-model
|
||||
if (needsModelSection && modelSection == null) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.missing_model");
|
||||
}
|
||||
|
||||
// 新版格式
|
||||
ItemModel modernModel = null;
|
||||
// 旧版格式
|
||||
TreeSet<LegacyOverridesModel> legacyOverridesModels = null;
|
||||
// 如果需要支持新版item model 或者用户需要旧版本兼容,但是没配置legacy-model
|
||||
if (needsModelSection) {
|
||||
modernModel = ItemModels.fromMap(modelSection);
|
||||
for (ModelGeneration generation : modernModel.modelsToGenerate()) {
|
||||
prepareModelGeneration(generation);
|
||||
// 1.21.4+必须要配置model区域,如果不需要高版本兼容,则可以只写legacy-model
|
||||
if (modelSection == null) {
|
||||
collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
modernModel = ItemModels.fromMap(modelSection);
|
||||
for (ModelGeneration generation : modernModel.modelsToGenerate()) {
|
||||
prepareModelGeneration(generation);
|
||||
}
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
collector.addAndThrow(e);
|
||||
}
|
||||
}
|
||||
// 如果需要旧版本兼容
|
||||
if (needsLegacyCompatibility()) {
|
||||
if (legacyModelSection != null) {
|
||||
LegacyItemModel legacyItemModel = LegacyItemModel.fromMap(legacyModelSection, customModelData);
|
||||
for (ModelGeneration generation : legacyItemModel.modelsToGenerate()) {
|
||||
prepareModelGeneration(generation);
|
||||
try {
|
||||
LegacyItemModel legacyItemModel = LegacyItemModel.fromMap(legacyModelSection, customModelData);
|
||||
for (ModelGeneration generation : legacyItemModel.modelsToGenerate()) {
|
||||
prepareModelGeneration(generation);
|
||||
}
|
||||
legacyOverridesModels = new TreeSet<>(legacyItemModel.overrides());
|
||||
} catch (LocalizedResourceConfigException e) {
|
||||
collector.addAndThrow(e);
|
||||
}
|
||||
legacyOverridesModels = new TreeSet<>(legacyItemModel.overrides());
|
||||
} else {
|
||||
legacyOverridesModels = new TreeSet<>();
|
||||
processModelRecursively(modernModel, new LinkedHashMap<>(), legacyOverridesModels, clientBoundMaterial, customModelData);
|
||||
if (legacyOverridesModels.isEmpty()) {
|
||||
TranslationManager.instance().log("warning.config.item.legacy_model.cannot_convert", path.toString(), id.asString());
|
||||
collector.add(new LocalizedResourceConfigException("warning.config.item.legacy_model.cannot_convert", path.toString(), id.asString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -457,7 +486,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
// 检查cmd冲突
|
||||
Map<Integer, Key> conflict = AbstractItemManager.this.cmdConflictChecker.computeIfAbsent(finalBaseModel, k -> new HashMap<>());
|
||||
if (conflict.containsKey(customModelData)) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.custom_model_data_conflict", String.valueOf(customModelData), conflict.get(customModelData).toString());
|
||||
collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.custom_model_data_conflict", String.valueOf(customModelData), conflict.get(customModelData).toString()));
|
||||
}
|
||||
conflict.put(customModelData, id);
|
||||
// 添加新版item model
|
||||
@@ -475,7 +504,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
lom.addAll(legacyOverridesModels);
|
||||
}
|
||||
} else if (isVanillaItemModel) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModelKey.asString());
|
||||
collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModelKey.asString()));
|
||||
}
|
||||
|
||||
// 使用了item-model组件,且不是原版物品的
|
||||
@@ -502,162 +531,9 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerFunctions() {
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
String plugin = data.get("plugin").toString();
|
||||
String id = data.get("id").toString();
|
||||
ExternalItemProvider<I> provider = AbstractItemManager.this.getExternalItemProvider(plugin);
|
||||
return new ExternalModifier<>(id, Objects.requireNonNull(provider, "Item provider " + plugin + " not found"));
|
||||
}, "external");
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
registerDataType((obj) -> {
|
||||
String name = obj.toString();
|
||||
return new CustomNameModifier<>(name);
|
||||
}, "custom-name");
|
||||
registerDataType((obj) -> {
|
||||
String name = obj.toString();
|
||||
return new ItemNameModifier<>(name);
|
||||
}, "item-name", "display-name");
|
||||
} else {
|
||||
registerDataType((obj) -> {
|
||||
String name = obj.toString();
|
||||
return new CustomNameModifier<>(name);
|
||||
}, "custom-name", "item-name", "display-name");
|
||||
}
|
||||
registerDataType((obj) -> {
|
||||
List<String> lore = MiscUtils.getAsStringList(obj);
|
||||
return new LoreModifier<>(lore);
|
||||
}, "lore", "display-lore", "description");
|
||||
registerDataType((obj) -> {
|
||||
Map<String, List<String>> dynamicLore = new LinkedHashMap<>();
|
||||
if (obj instanceof Map<?, ?> map) {
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
dynamicLore.put(entry.getKey().toString(), MiscUtils.getAsStringList(entry.getValue()));
|
||||
}
|
||||
}
|
||||
return new DynamicLoreModifier<>(dynamicLore);
|
||||
}, "dynamic-lore");
|
||||
registerDataType((obj) -> {
|
||||
if (obj instanceof Integer integer) {
|
||||
return new DyedColorModifier<>(integer);
|
||||
} else {
|
||||
Vector3f vector3f = MiscUtils.getAsVector3f(obj, "dyed-color");
|
||||
return new DyedColorModifier<>(0 << 24 /*不可省略*/ | MCUtils.fastFloor(vector3f.x) << 16 | MCUtils.fastFloor(vector3f.y) << 8 | MCUtils.fastFloor(vector3f.z));
|
||||
}
|
||||
}, "dyed-color");
|
||||
if (!VersionHelper.isOrAbove1_21_5()) {
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
return new TagsModifier<>(data);
|
||||
}, "tags", "tag", "nbt");
|
||||
}
|
||||
registerDataType((object -> {
|
||||
MutableInt mutableInt = new MutableInt(0);
|
||||
List<AttributeModifier> attributeModifiers = ResourceConfigUtils.parseConfigAsList(object, (map) -> {
|
||||
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.data.attribute_modifiers.missing_type");
|
||||
Key nativeType = AttributeModifiersModifier.getNativeAttributeName(Key.of(type));
|
||||
AttributeModifier.Slot slot = AttributeModifier.Slot.valueOf(map.getOrDefault("slot", "any").toString().toUpperCase(Locale.ENGLISH));
|
||||
Key id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of).orElseGet(() -> {
|
||||
mutableInt.add(1);
|
||||
return Key.of("craftengine", "modifier_" + mutableInt.intValue());
|
||||
});
|
||||
double amount = ResourceConfigUtils.getAsDouble(
|
||||
ResourceConfigUtils.requireNonNullOrThrow(map.get("amount"), "warning.config.item.data.attribute_modifiers.missing_amount"), "amount"
|
||||
);
|
||||
AttributeModifier.Operation operation = AttributeModifier.Operation.valueOf(
|
||||
ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("operation"), "warning.config.item.data.attribute_modifiers.missing_operation").toUpperCase(Locale.ENGLISH)
|
||||
);
|
||||
AttributeModifier.Display display = null;
|
||||
if (VersionHelper.isOrAbove1_21_6() && map.containsKey("display")) {
|
||||
Map<String, Object> displayMap = MiscUtils.castToMap(map.get("display"), false);
|
||||
AttributeModifier.Display.Type displayType = AttributeModifier.Display.Type.valueOf(ResourceConfigUtils.requireNonEmptyStringOrThrow(displayMap.get("type"), "warning.config.item.data.attribute_modifiers.display.missing_type").toUpperCase(Locale.ENGLISH));
|
||||
if (displayType == AttributeModifier.Display.Type.OVERRIDE) {
|
||||
String miniMessageValue = ResourceConfigUtils.requireNonEmptyStringOrThrow(displayMap.get("value"), "warning.config.item.data.attribute_modifiers.display.missing_value");
|
||||
display = new AttributeModifier.Display(displayType, miniMessageValue);
|
||||
} else {
|
||||
display = new AttributeModifier.Display(displayType, null);
|
||||
}
|
||||
}
|
||||
return new AttributeModifier(nativeType.value(), slot, id,
|
||||
amount, operation, display);
|
||||
});
|
||||
return new AttributeModifiersModifier<>(attributeModifiers);
|
||||
}), "attributes", "attribute-modifiers", "attribute-modifier");
|
||||
registerDataType((obj) -> {
|
||||
boolean value = TypeUtils.checkType(obj, Boolean.class);
|
||||
return new UnbreakableModifier<>(value);
|
||||
}, "unbreakable");
|
||||
registerDataType((obj) -> {
|
||||
int customModelData = ResourceConfigUtils.getAsInt(obj, "custom-model-data");
|
||||
return new CustomModelDataModifier<>(customModelData);
|
||||
}, "custom-model-data");
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
List<Enchantment> enchantments = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> e : data.entrySet()) {
|
||||
if (e.getValue() instanceof Number number) {
|
||||
enchantments.add(new Enchantment(Key.of(e.getKey()), number.intValue()));
|
||||
}
|
||||
}
|
||||
return new EnchantmentModifier<>(enchantments);
|
||||
}, "enchantment", "enchantments", "enchant");
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
String material = data.get("material").toString().toLowerCase(Locale.ENGLISH);
|
||||
String pattern = data.get("pattern").toString().toLowerCase(Locale.ENGLISH);
|
||||
return new TrimModifier<>(Key.of(material), Key.of(pattern));
|
||||
}, "trim");
|
||||
registerDataType((obj) -> {
|
||||
List<Key> components = MiscUtils.getAsStringList(obj).stream().map(Key::of).toList();
|
||||
return new HideTooltipModifier<>(components);
|
||||
}, "hide-tooltip", "hide-flags");
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
Map<String, TextProvider> arguments = new HashMap<>();
|
||||
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||
arguments.put(entry.getKey(), TextProviders.fromString(entry.getValue().toString()));
|
||||
}
|
||||
return new ArgumentModifier<>(arguments);
|
||||
}, "args", "argument", "arguments");
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
return new ComponentModifier<>(data);
|
||||
}, "components", "component");
|
||||
registerDataType((obj) -> {
|
||||
List<String> data = MiscUtils.getAsStringList(obj);
|
||||
return new RemoveComponentModifier<>(data);
|
||||
}, "remove-components", "remove-component");
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
int nutrition = ResourceConfigUtils.getAsInt(data.get("nutrition"), "nutrition");
|
||||
float saturation = ResourceConfigUtils.getAsFloat(data.get("saturation"), "saturation");
|
||||
return new FoodModifier<>(nutrition, saturation, ResourceConfigUtils.getAsBoolean(data.getOrDefault("can-always-eat", false), "can-always-eat"));
|
||||
}, "food");
|
||||
}
|
||||
if (VersionHelper.isOrAbove1_21()) {
|
||||
registerDataType((obj) -> {
|
||||
String song = obj.toString();
|
||||
return new JukeboxSongModifier<>(new JukeboxPlayable(song, true));
|
||||
}, "jukebox-playable");
|
||||
}
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
registerDataType((obj) -> {
|
||||
String id = obj.toString();
|
||||
return new TooltipStyleModifier<>(Key.of(id));
|
||||
}, "tooltip-style");
|
||||
registerDataType((obj) -> {
|
||||
Map<String, Object> data = MiscUtils.castToMap(obj, false);
|
||||
return new EquippableModifier<>(EquipmentData.fromMap(data));
|
||||
}, "equippable");
|
||||
registerDataType((obj) -> {
|
||||
String id = obj.toString();
|
||||
return new ItemModelModifier<>(Key.of(id));
|
||||
}, "item-model");
|
||||
// 抛出异常
|
||||
collector.throwIfPresent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,16 @@ public interface BuildableItem<I> {
|
||||
|
||||
Key id();
|
||||
|
||||
Item<I> buildItem(ItemBuildContext context, int count);
|
||||
|
||||
default Item<I> buildItem(Player player) {
|
||||
return buildItem(ItemBuildContext.of(player));
|
||||
}
|
||||
|
||||
default Item<I> buildItem(ItemBuildContext context) {
|
||||
return buildItem(context, 1);
|
||||
}
|
||||
|
||||
I buildItemStack(ItemBuildContext context, int count);
|
||||
|
||||
default I buildItemStack(ItemBuildContext context) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public class CloneableConstantItem<I> implements BuildableItem<I> {
|
||||
private final Item<I> item;
|
||||
|
||||
private CloneableConstantItem(Item<I> item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public static <I> CloneableConstantItem<I> of(Item<I> item) {
|
||||
return new CloneableConstantItem<>(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key id() {
|
||||
return this.item.id();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<I> buildItem(ItemBuildContext context, int count) {
|
||||
return this.item.copyWithCount(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public I buildItemStack(ItemBuildContext context, int count) {
|
||||
return this.item.copyWithCount(count).getItem();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||
@@ -15,6 +14,8 @@ import java.util.Map;
|
||||
|
||||
public interface CustomItem<I> extends BuildableItem<I> {
|
||||
|
||||
boolean isVanillaItem();
|
||||
|
||||
Key id();
|
||||
|
||||
UniqueKey uniqueId();
|
||||
@@ -35,18 +36,14 @@ public interface CustomItem<I> extends BuildableItem<I> {
|
||||
return settings().tags().contains(tag);
|
||||
}
|
||||
|
||||
default Item<I> buildItem(Player player) {
|
||||
return buildItem(ItemBuildContext.of(player));
|
||||
}
|
||||
|
||||
Item<I> buildItem(ItemBuildContext context);
|
||||
|
||||
void execute(PlayerOptionalContext context, EventTrigger trigger);
|
||||
|
||||
@NotNull
|
||||
List<ItemBehavior> behaviors();
|
||||
|
||||
interface Builder<I> {
|
||||
Builder<I> isVanillaItem(boolean isVanillaItem);
|
||||
|
||||
Builder<I> id(UniqueKey id);
|
||||
|
||||
Builder<I> clientBoundMaterial(Key clientBoundMaterialKey);
|
||||
|
||||
@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface ExternalItemProvider<I> {
|
||||
public interface ExternalItemSource<I> {
|
||||
|
||||
String plugin();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user