镜像样板供应器

This commit is contained in:
GaLi 2026-03-23 14:02:09 +08:00
parent 1ee757e84c
commit af9f75082e
29 changed files with 974 additions and 3 deletions

View File

@ -0,0 +1,35 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_mirror_pattern_binding_tool": {
"conditions": {
"items": [
{
"items": [
"extendedae_plus:mirror_pattern_binding_tool"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "extendedae_plus:mirror_pattern_binding_tool"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_mirror_pattern_binding_tool",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"extendedae_plus:mirror_pattern_binding_tool"
]
},
"sends_telemetry_event": false
}

View File

@ -0,0 +1,35 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_mirror_pattern_provider": {
"conditions": {
"items": [
{
"items": [
"extendedae_plus:mirror_pattern_provider"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "extendedae_plus:mirror_pattern_provider"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_mirror_pattern_provider",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"extendedae_plus:mirror_pattern_provider"
]
},
"sends_telemetry_event": false
}

View File

@ -0,0 +1,27 @@
{
"type": "minecraft:crafting_shaped",
"category": "misc",
"key": {
"A": {
"item": "ae2:wireless_receiver"
},
"B": {
"item": "minecraft:iron_ingot"
},
"C": {
"item": "minecraft:redstone"
},
"D": {
"item": "ae2:calculation_processor"
}
},
"pattern": [
" A",
"BCD",
"BBB"
],
"result": {
"item": "extendedae_plus:mirror_pattern_binding_tool"
},
"show_notification": true
}

View File

@ -0,0 +1,21 @@
{
"type": "minecraft:crafting_shaped",
"category": "misc",
"key": {
"A": {
"item": "minecraft:glass"
},
"B": {
"item": "ae2:pattern_provider"
}
},
"pattern": [
"AAA",
"ABA",
"AAA"
],
"result": {
"item": "extendedae_plus:mirror_pattern_provider"
},
"show_notification": true
}

View File

@ -1,11 +1,13 @@
package com.extendedae_plus;
import appeng.api.storage.StorageCells;
import appeng.block.AEBaseEntityBlock;
import appeng.menu.locator.MenuLocators;
import com.extendedae_plus.api.storage.InfinityBigIntegerCellHandler;
import com.extendedae_plus.client.ClientRegistrar;
import com.extendedae_plus.client.ModKeybindings;
import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity;
import com.extendedae_plus.content.matrix.CrafterCorePlusBlockEntity;
import com.extendedae_plus.content.matrix.PatternCorePlusBlockEntity;
import com.extendedae_plus.init.*;
@ -106,6 +108,17 @@ public class ExtendedAEPlus {
null
);
((AEBaseEntityBlock) ModBlocks.MIRROR_PATTERN_PROVIDER.get()).setBlockEntity(
MirrorPatternProviderBlockEntity.class,
ModBlockEntities.MIRROR_PATTERN_PROVIDER_BE.get(),
null,
(level, pos, state, blockEntity) -> MirrorPatternProviderBlockEntity.serverTick(
level,
pos,
state,
(MirrorPatternProviderBlockEntity) blockEntity)
);
});
}

View File

@ -0,0 +1,52 @@
package com.extendedae_plus.content.ae2;
import appeng.api.implementations.items.IMemoryCard;
import appeng.block.crafting.PatternProviderBlock;
import appeng.util.InteractionUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.Nullable;
public class MirrorPatternProviderBlock extends PatternProviderBlock {
@Override
public InteractionResult onActivated(Level level, BlockPos pos, Player player, InteractionHand hand,
@Nullable ItemStack heldItem, BlockHitResult hit) {
var mirror = this.getMirror(level, pos);
if (mirror == null) {
return InteractionResult.PASS;
}
if (InteractionUtil.isInAlternateUseMode(player)) {
return InteractionResult.PASS;
}
if (heldItem != null
&& (InteractionUtil.canWrenchRotate(heldItem) || heldItem.getItem() instanceof IMemoryCard)) {
if (!level.isClientSide) {
player.displayClientMessage(
net.minecraft.network.chat.Component.translatable(
"extendedae_plus.message.mirror_pattern_provider.readonly"),
true);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
if (!level.isClientSide) {
player.displayClientMessage(mirror.getStatusMessage(), true);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
@Nullable
private MirrorPatternProviderBlockEntity getMirror(Level level, BlockPos pos) {
var blockEntity = this.getBlockEntity(level, pos);
return blockEntity instanceof MirrorPatternProviderBlockEntity mirror ? mirror : null;
}
}

View File

@ -0,0 +1,432 @@
package com.extendedae_plus.content.ae2;
import appeng.api.config.Settings;
import appeng.api.inventories.InternalInventory;
import appeng.api.networking.IManagedGridNode;
import appeng.block.crafting.PatternProviderBlock;
import appeng.block.crafting.PushDirection;
import appeng.blockentity.crafting.PatternProviderBlockEntity;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.util.CustomNameUtil;
import appeng.util.SettingsFrom;
import appeng.util.inv.AppEngInternalInventory;
import com.extendedae_plus.init.ModBlockEntities;
import net.minecraft.core.BlockPos;
import net.minecraft.core.GlobalPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
public class MirrorPatternProviderBlockEntity extends PatternProviderBlockEntity {
private static final String TAG_MASTER = "mirrorMaster";
private static final String TAG_MASTER_DIMENSION = "dimension";
private static final String TAG_MASTER_POS = "pos";
private static final int SYNC_INTERVAL = 2;
private static final InternalInventory DISABLED_PATTERN_INVENTORY = new AppEngInternalInventory(0);
@Nullable
private ResourceKey<Level> masterDimension;
@Nullable
private BlockPos masterPos;
public MirrorPatternProviderBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.MIRROR_PATTERN_PROVIDER_BE.get(), pos, blockState);
}
@Override
public boolean isVisibleInTerminal() {
return false;
}
@Override
public InternalInventory getTerminalPatternInventory() {
return DISABLED_PATTERN_INVENTORY;
}
@Override
protected PatternProviderLogic createLogic() {
return new MirrorLogic(this.getMainNode(), this);
}
public static void serverTick(Level level, BlockPos pos, BlockState state,
MirrorPatternProviderBlockEntity blockEntity) {
if (level instanceof ServerLevel serverLevel) {
blockEntity.serverTick(serverLevel);
}
}
private void serverTick(ServerLevel level) {
if (Math.floorMod(level.getGameTime() + this.getBlockPos().asLong(), SYNC_INTERVAL) != 0) {
return;
}
this.syncBoundMaster();
}
@Override
public void onReady() {
super.onReady();
if (this.getLevel() instanceof ServerLevel serverLevel) {
this.syncBoundMaster();
}
}
@Override
public void saveAdditional(CompoundTag data) {
super.saveAdditional(data);
if (this.masterDimension != null && this.masterPos != null) {
var masterTag = new CompoundTag();
masterTag.putString(TAG_MASTER_DIMENSION, this.masterDimension.location().toString());
masterTag.putLong(TAG_MASTER_POS, this.masterPos.asLong());
data.put(TAG_MASTER, masterTag);
} else {
data.remove(TAG_MASTER);
}
}
@Override
public void loadTag(CompoundTag data) {
super.loadTag(data);
this.masterDimension = null;
this.masterPos = null;
if (data.contains(TAG_MASTER, Tag.TAG_COMPOUND)) {
var masterTag = data.getCompound(TAG_MASTER);
if (masterTag.contains(TAG_MASTER_DIMENSION, Tag.TAG_STRING)
&& masterTag.contains(TAG_MASTER_POS, Tag.TAG_LONG)) {
this.masterDimension = ResourceKey.create(Registries.DIMENSION,
new ResourceLocation(masterTag.getString(TAG_MASTER_DIMENSION)));
this.masterPos = BlockPos.of(masterTag.getLong(TAG_MASTER_POS));
}
}
}
@Override
public void addAdditionalDrops(Level level, BlockPos pos, List<ItemStack> drops) {
var patternInventory = this.getPatternInventory();
var snapshot = this.copyInventoryContents(patternInventory);
this.clearInventory(patternInventory);
this.getLogic().updatePatterns();
super.addAdditionalDrops(level, pos, drops);
this.restoreInventoryContents(patternInventory, snapshot);
this.getLogic().updatePatterns();
}
@Override
public void clearContent() {
this.getLogic().clearContent();
}
public boolean bindToMaster(GlobalPos master) {
if (!(this.getLevel() instanceof ServerLevel serverLevel)) {
return false;
}
if (master.pos().equals(this.getBlockPos()) && master.dimension().equals(serverLevel.dimension())) {
return false;
}
var masterLevel = serverLevel.getServer().getLevel(master.dimension());
if (masterLevel != null && masterLevel.hasChunkAt(master.pos())) {
var blockEntity = masterLevel.getBlockEntity(master.pos());
if (isValidMaster(blockEntity)) {
return this.syncFromMaster((PatternProviderBlockEntity) blockEntity);
}
return false;
}
var changed = !Objects.equals(this.masterDimension, master.dimension())
|| !Objects.equals(this.masterPos, master.pos());
this.masterDimension = master.dimension();
this.masterPos = master.pos().immutable();
if (changed) {
this.saveChanges();
this.markForUpdate();
}
return true;
}
@Nullable
public PatternProviderBlockEntity getMaster() {
if (this.masterDimension == null || this.masterPos == null || !(this.getLevel() instanceof ServerLevel serverLevel)) {
return null;
}
var masterLevel = this.getMasterLevel(serverLevel);
if (masterLevel == null || !masterLevel.hasChunkAt(this.masterPos)) {
return null;
}
var blockEntity = masterLevel.getBlockEntity(this.masterPos);
return isValidMaster(blockEntity) ? (PatternProviderBlockEntity) blockEntity : null;
}
public Component createBoundMessage() {
if (this.masterPos != null) {
return Component.translatable(
"extendedae_plus.message.mirror_pattern_provider.bound",
this.masterPos.getX(),
this.masterPos.getY(),
this.masterPos.getZ());
}
return Component.translatable("extendedae_plus.message.mirror_pattern_provider.missing_master");
}
public Component getStatusMessage() {
if (this.masterPos != null) {
return Component.translatable(
"extendedae_plus.message.mirror_pattern_provider.following",
this.masterPos.getX(),
this.masterPos.getY(),
this.masterPos.getZ());
}
return Component.translatable("extendedae_plus.message.mirror_pattern_provider.missing_master");
}
private void syncBoundMaster() {
var master = this.getMaster();
if (master != null) {
this.syncFromMaster(master);
return;
}
if (this.shouldClearBrokenBinding()) {
this.clearMasterBinding(true);
}
}
private boolean shouldClearBrokenBinding() {
if (this.masterDimension == null || this.masterPos == null || !(this.getLevel() instanceof ServerLevel serverLevel)) {
return true;
}
var masterLevel = this.getMasterLevel(serverLevel);
if (masterLevel == null || !masterLevel.hasChunkAt(this.masterPos)) {
return false;
}
return !isValidMaster(masterLevel.getBlockEntity(this.masterPos));
}
private void clearMasterBinding(boolean clearMirroredPatterns) {
var hadBinding = this.masterDimension != null || this.masterPos != null;
this.masterDimension = null;
this.masterPos = null;
var changed = hadBinding;
if (clearMirroredPatterns) {
changed |= this.clearMirroredPatterns();
}
if (changed) {
this.saveChanges();
this.markForUpdate();
}
}
private boolean bindToMaster(PatternProviderBlockEntity master) {
var masterLevel = master.getLevel();
if (masterLevel == null) {
return false;
}
var newDimension = masterLevel.dimension();
var newPos = master.getBlockPos().immutable();
var changed = !Objects.equals(this.masterDimension, newDimension) || !Objects.equals(this.masterPos, newPos);
this.masterDimension = newDimension;
this.masterPos = newPos;
return changed;
}
private boolean syncFromMaster(PatternProviderBlockEntity master) {
var changed = this.bindToMaster(master);
changed |= this.syncMirroredSettings(master);
changed |= this.syncMirroredPatterns(master);
if (changed) {
this.saveChanges();
this.markForUpdate();
}
return changed;
}
private boolean syncMirroredSettings(PatternProviderBlockEntity master) {
if (!this.hasDifferentMirroredSettings(master)) {
return false;
}
var settingsTag = new CompoundTag();
master.getLogic().getConfigManager().writeToNBT(settingsTag);
settingsTag.putByte(PatternProviderBlock.PUSH_DIRECTION.getName(),
(byte) master.getBlockState().getValue(PatternProviderBlock.PUSH_DIRECTION).ordinal());
CustomNameUtil.setCustomName(settingsTag, master.getCustomName());
super.importSettings(SettingsFrom.MEMORY_CARD, settingsTag, null);
this.getLogic().getConfigManager().readFromNBT(settingsTag);
if (this.getPriority() != master.getPriority()) {
this.setPriority(master.getPriority());
}
return true;
}
private boolean hasDifferentMirroredSettings(PatternProviderBlockEntity master) {
var mirrorLogic = this.getLogic();
var masterLogic = master.getLogic();
return !Objects.equals(this.getCustomName(), master.getCustomName())
|| this.getPriority() != master.getPriority()
|| mirrorLogic.getConfigManager().getSetting(Settings.BLOCKING_MODE)
!= masterLogic.getConfigManager().getSetting(Settings.BLOCKING_MODE)
|| mirrorLogic.getConfigManager().getSetting(Settings.PATTERN_ACCESS_TERMINAL)
!= masterLogic.getConfigManager().getSetting(Settings.PATTERN_ACCESS_TERMINAL)
|| mirrorLogic.getConfigManager().getSetting(Settings.LOCK_CRAFTING_MODE)
!= masterLogic.getConfigManager().getSetting(Settings.LOCK_CRAFTING_MODE)
|| this.getBlockState().getValue(PatternProviderBlock.PUSH_DIRECTION)
!= master.getBlockState().getValue(PatternProviderBlock.PUSH_DIRECTION);
}
private boolean syncMirroredPatterns(PatternProviderBlockEntity master) {
var mirrorInventory = this.getPatternInventory();
var masterInventory = asPatternInventory(master.getLogic().getPatternInv());
if (this.hasSamePatterns(masterInventory, mirrorInventory)) {
return false;
}
this.clearInventory(mirrorInventory);
for (int slot = 0; slot < masterInventory.size(); slot++) {
mirrorInventory.setItemDirect(slot, masterInventory.getStackInSlot(slot).copy());
}
this.getLogic().updatePatterns();
return true;
}
private boolean clearMirroredPatterns() {
var patternInventory = this.getPatternInventory();
if (this.isPatternInventoryEmpty(patternInventory)) {
return false;
}
this.clearInventory(patternInventory);
this.getLogic().updatePatterns();
return true;
}
private boolean hasSamePatterns(AppEngInternalInventory masterInventory, AppEngInternalInventory mirrorInventory) {
if (masterInventory.size() != mirrorInventory.size()) {
return false;
}
for (int slot = 0; slot < masterInventory.size(); slot++) {
if (!sameStack(masterInventory.getStackInSlot(slot), mirrorInventory.getStackInSlot(slot))) {
return false;
}
}
return true;
}
private boolean isPatternInventoryEmpty(AppEngInternalInventory inventory) {
for (int slot = 0; slot < inventory.size(); slot++) {
if (!inventory.getStackInSlot(slot).isEmpty()) {
return false;
}
}
return true;
}
private AppEngInternalInventory getPatternInventory() {
return ((MirrorLogic) this.getLogic()).getActualPatternInventory();
}
private ItemStack[] copyInventoryContents(AppEngInternalInventory inventory) {
var contents = new ItemStack[inventory.size()];
for (int slot = 0; slot < inventory.size(); slot++) {
contents[slot] = inventory.getStackInSlot(slot).copy();
}
return contents;
}
private void restoreInventoryContents(AppEngInternalInventory inventory, ItemStack[] contents) {
this.clearInventory(inventory);
for (int slot = 0; slot < inventory.size(); slot++) {
inventory.setItemDirect(slot, contents[slot].copy());
}
}
private void clearInventory(AppEngInternalInventory inventory) {
for (int slot = 0; slot < inventory.size(); slot++) {
inventory.setItemDirect(slot, ItemStack.EMPTY);
}
}
@Nullable
private ServerLevel getMasterLevel(ServerLevel serverLevel) {
if (serverLevel.dimension() == this.masterDimension) {
return serverLevel;
}
return serverLevel.getServer().getLevel(this.masterDimension);
}
private static AppEngInternalInventory asPatternInventory(Object inventory) {
return (AppEngInternalInventory) inventory;
}
private static boolean sameStack(ItemStack left, ItemStack right) {
if (left.isEmpty() && right.isEmpty()) {
return true;
}
return ItemStack.isSameItemSameTags(left, right) && left.getCount() == right.getCount();
}
public static boolean isSupportedMaster(@Nullable BlockEntity blockEntity) {
return blockEntity != null
&& blockEntity.getClass() == PatternProviderBlockEntity.class
&& !blockEntity.isRemoved();
}
private static boolean isValidMaster(@Nullable BlockEntity blockEntity) {
return isSupportedMaster(blockEntity);
}
private static final class MirrorLogic extends PatternProviderLogic {
private MirrorLogic(IManagedGridNode mainNode, MirrorPatternProviderBlockEntity mirrorHost) {
super(mainNode, mirrorHost);
}
@Override
public InternalInventory getPatternInv() {
return DISABLED_PATTERN_INVENTORY;
}
private AppEngInternalInventory getActualPatternInventory() {
return (AppEngInternalInventory) super.getPatternInv();
}
}
}

View File

@ -124,6 +124,28 @@ public class CraftingRecipes extends RecipeProvider {
.define('B', EPPItemAndBlock.ASSEMBLER_MATRIX_WALL)
.unlockedBy("has_quantum_ring", has(AEBlocks.QUANTUM_RING))
.save(consumer);
//镜像样板供应器
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.MIRROR_PATTERN_PROVIDER.get())
.pattern("AAA")
.pattern("ABA")
.pattern("AAA")
.unlockedBy("has_mirror_pattern_provider",has(ModItems.MIRROR_PATTERN_PROVIDER.get()))
.define('A',Items.GLASS)
.define('B',AEBlocks.PATTERN_PROVIDER)
.save(consumer);
//镜像样板绑定工具
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.MIRROR_PATTERN_BINDING_TOOL.get())
.pattern(" A")
.pattern("BCD")
.pattern("BBB")
.unlockedBy("has_mirror_pattern_binding_tool",has(ModItems.MIRROR_PATTERN_BINDING_TOOL.get()))
.define('A', AEItems.WIRELESS_RECEIVER)
.define('B', Items.IRON_INGOT)
.define('C', Items.REDSTONE)
.define('D', AEItems.CALCULATION_PROCESSOR)
.save(consumer);
}
private void addCraftingAccelerators(Consumer<FinishedRecipe> consumer) {

View File

@ -1,6 +1,7 @@
package com.extendedae_plus.init;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity;
import com.extendedae_plus.content.matrix.CrafterCorePlusBlockEntity;
import com.extendedae_plus.content.matrix.PatternCorePlusBlockEntity;
import com.extendedae_plus.content.matrix.SpeedCorePlusBlockEntity;
@ -58,4 +59,9 @@ public final class ModBlockEntities {
BLOCK_ENTITY_TYPES.register("assembler_matrix_upload_core",
() -> BlockEntityType.Builder.of(UploadCoreBlockEntity::new,
ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get()).build(null));
public static final RegistryObject<BlockEntityType<MirrorPatternProviderBlockEntity>> MIRROR_PATTERN_PROVIDER_BE =
BLOCK_ENTITY_TYPES.register("mirror_pattern_provider",
() -> BlockEntityType.Builder.of(MirrorPatternProviderBlockEntity::new,
ModBlocks.MIRROR_PATTERN_PROVIDER.get()).build(null));
}

View File

@ -4,6 +4,7 @@ import appeng.block.crafting.CraftingUnitBlock;
import appeng.blockentity.crafting.CraftingBlockEntity;
import appeng.core.definitions.AEBlockEntities;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlock;
import com.extendedae_plus.content.crafting.EPlusCraftingUnitType;
import com.extendedae_plus.content.matrix.CrafterCorePlusBlock;
import com.extendedae_plus.content.matrix.PatternCorePlusBlock;
@ -126,4 +127,9 @@ public final class ModBlocks {
return b;
}
);
public static final RegistryObject<MirrorPatternProviderBlock> MIRROR_PATTERN_PROVIDER = BLOCKS.register(
"mirror_pattern_provider",
MirrorPatternProviderBlock::new
);
}

View File

@ -27,6 +27,8 @@ public final class ModCreativeTabs {
output.accept(ModItems.ASSEMBLER_MATRIX_CRAFTER_PLUS.get());
//超级装配矩阵样板核心
output.accept(ModItems.ASSEMBLER_MATRIX_PATTERN_PLUS.get());
output.accept(ModItems.MIRROR_PATTERN_PROVIDER.get());
output.accept(ModItems.MIRROR_PATTERN_BINDING_TOOL.get());
//实体加速器&加速卡

View File

@ -10,6 +10,7 @@ import com.extendedae_plus.items.InfinityBigIntegerCellItem;
import com.extendedae_plus.items.materials.ChannelCardItem;
import com.extendedae_plus.items.materials.EntitySpeedCardItem;
import com.extendedae_plus.items.materials.VirtualCraftingCardItem;
import com.extendedae_plus.items.tools.MirrorPatternBindingToolItem;
import com.extendedae_plus.util.ModCheckUtils;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
@ -85,6 +86,16 @@ public final class ModItems {
() -> new BlockItem(ModBlocks.CRAFTING_ACCELERATOR_1024x.get(), new Item.Properties())
);
public static final RegistryObject<Item> MIRROR_PATTERN_PROVIDER = ITEMS.register(
"mirror_pattern_provider",
() -> new BlockItem(ModBlocks.MIRROR_PATTERN_PROVIDER.get(), new Item.Properties())
);
public static final RegistryObject<MirrorPatternBindingToolItem> MIRROR_PATTERN_BINDING_TOOL = ITEMS.register(
"mirror_pattern_binding_tool",
() -> new MirrorPatternBindingToolItem(new Item.Properties())
);
public static final RegistryObject<EntitySpeedTickerPartItem> ENTITY_TICKER_PART_ITEM = ITEMS.register(
"entity_speed_ticker",
() -> new EntitySpeedTickerPartItem(new Item.Properties())

View File

@ -0,0 +1,177 @@
package com.extendedae_plus.items.tools;
import appeng.blockentity.crafting.PatternProviderBlockEntity;
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.GlobalPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class MirrorPatternBindingToolItem extends Item {
private static final String TAG_SELECTED_MASTER = "selectedMaster";
private static final String TAG_DIMENSION = "dimension";
private static final String TAG_POS = "pos";
public MirrorPatternBindingToolItem(Properties properties) {
super(properties.stacksTo(1));
}
@Override
public InteractionResult useOn(UseOnContext context) {
var level = context.getLevel();
var player = context.getPlayer();
var blockEntity = level.getBlockEntity(context.getClickedPos());
var stack = context.getItemInHand();
if (blockEntity instanceof PatternProviderBlockEntity
&& !(blockEntity instanceof MirrorPatternProviderBlockEntity)) {
if (player != null && player.isShiftKeyDown()) {
if (!level.isClientSide) {
if (MirrorPatternProviderBlockEntity.isSupportedMaster(blockEntity)) {
setSelectedMaster(stack, GlobalPos.of(level.dimension(), context.getClickedPos()));
player.displayClientMessage(
Component.translatable(
"extendedae_plus.message.mirror_binding_tool.selected",
context.getClickedPos().getX(),
context.getClickedPos().getY(),
context.getClickedPos().getZ()),
true);
} else {
player.displayClientMessage(
Component.translatable(
"extendedae_plus.message.mirror_binding_tool.only_normal_provider"),
true);
}
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
return InteractionResult.PASS;
}
if (blockEntity instanceof MirrorPatternProviderBlockEntity mirror) {
if (!level.isClientSide) {
var selectedMaster = getSelectedMaster(stack);
if (selectedMaster == null) {
if (player != null) {
player.displayClientMessage(
Component.translatable("extendedae_plus.message.mirror_binding_tool.no_selection"),
true);
}
return InteractionResult.SUCCESS;
}
if (mirror.bindToMaster(selectedMaster)) {
if (player != null) {
player.displayClientMessage(mirror.createBoundMessage(), true);
}
} else if (player != null) {
player.displayClientMessage(
Component.translatable("extendedae_plus.message.mirror_binding_tool.bind_failed"),
true);
}
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
return InteractionResult.PASS;
}
@Override
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltipComponents,
TooltipFlag tooltipFlag) {
super.appendHoverText(stack, level, tooltipComponents, tooltipFlag);
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.tip.select"));
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.tip.bind"));
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.tip.clear"));
var selectedMaster = getSelectedMaster(stack);
if (selectedMaster != null) {
var pos = selectedMaster.pos();
tooltipComponents.add(Component.translatable(
"item.extendedae_plus.mirror_pattern_binding_tool.selected",
pos.getX(),
pos.getY(),
pos.getZ()));
tooltipComponents.add(Component.translatable(
"item.extendedae_plus.mirror_pattern_binding_tool.dimension",
selectedMaster.dimension().location().toString()));
} else {
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.unset"));
}
}
private static void setSelectedMaster(ItemStack stack, GlobalPos master) {
var tag = stack.getOrCreateTag();
var selectedTag = new CompoundTag();
selectedTag.putString(TAG_DIMENSION, master.dimension().location().toString());
selectedTag.putLong(TAG_POS, master.pos().asLong());
tag.put(TAG_SELECTED_MASTER, selectedTag);
}
private static void clearSelectedMaster(ItemStack stack) {
var tag = stack.getTag();
if (tag != null) {
tag.remove(TAG_SELECTED_MASTER);
}
}
@Nullable
private static GlobalPos getSelectedMaster(ItemStack stack) {
var tag = stack.getTag();
if (tag == null || !tag.contains(TAG_SELECTED_MASTER, Tag.TAG_COMPOUND)) {
return null;
}
var selectedTag = tag.getCompound(TAG_SELECTED_MASTER);
if (!selectedTag.contains(TAG_DIMENSION, Tag.TAG_STRING) || !selectedTag.contains(TAG_POS, Tag.TAG_LONG)) {
return null;
}
return GlobalPos.of(
ResourceKey.create(Registries.DIMENSION, new ResourceLocation(selectedTag.getString(TAG_DIMENSION))),
BlockPos.of(selectedTag.getLong(TAG_POS)));
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
var stack = player.getItemInHand(usedHand);
if (!player.isShiftKeyDown()) {
return InteractionResultHolder.pass(stack);
}
if (!level.isClientSide) {
if (getSelectedMaster(stack) != null) {
clearSelectedMaster(stack);
player.displayClientMessage(
Component.translatable("extendedae_plus.message.mirror_binding_tool.cleared"),
true);
} else {
player.displayClientMessage(
Component.translatable("extendedae_plus.message.mirror_binding_tool.no_selection"),
true);
}
}
return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
}
}

View File

@ -6,6 +6,7 @@ import appeng.api.upgrades.UpgradeInventories;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.helpers.patternprovider.PatternProviderLogicHost;
import com.extendedae_plus.compat.UpgradeSlotCompat;
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity;
import com.extendedae_plus.util.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -13,6 +14,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 当appflux存在时修改PatternProviderLogic的升级槽数量为2个
@ -28,26 +30,37 @@ public class AppfluxPatternProviderLogicMixin {
at = @At("TAIL"))
private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
try {
if (host instanceof MirrorPatternProviderBlockEntity) {
return;
}
// 只有当appflux存在且不启用我们的升级槽时才修改数量
if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) {
// 使用反射找到appflux的升级槽字段并替换
try {
Field upgradesField = this.getClass().getDeclaredField("af_$upgrades");
Field upgradesField = PatternProviderLogic.class.getDeclaredField("af_$upgrades");
upgradesField.setAccessible(true);
IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this);
Method onUpgradesChanged = null;
try {
onUpgradesChanged = PatternProviderLogic.class.getDeclaredMethod("af_$onUpgradesChanged");
onUpgradesChanged.setAccessible(true);
} catch (NoSuchMethodException ignored) {
}
if (currentUpgrades != null) {
// 创建新的2槽升级槽
Method finalOnUpgradesChanged = onUpgradesChanged;
IUpgradeInventory newUpgrades = UpgradeInventories.forMachine(
host.getTerminalIcon().getItem(),
2,
() -> {
try {
// 调用appflux的升级变更方法
this.getClass().getDeclaredMethod("af_$onUpgradesChanged").invoke(this);
if (finalOnUpgradesChanged != null) {
finalOnUpgradesChanged.invoke(this);
}
} catch (Exception e) {
Logger.EAP$LOGGER.error("调用appflux升级变更方法失败", e);
}
@ -64,6 +77,8 @@ public class AppfluxPatternProviderLogicMixin {
// 替换升级槽
upgradesField.set(this, newUpgrades);
}
} catch (NoSuchFieldException e) {
Logger.EAP$LOGGER.debug("未找到appflux升级槽字段跳过升级槽兼容调整");
} catch (Exception e) {
Logger.EAP$LOGGER.error("反射修改appflux升级槽失败", e);
}

View File

@ -0,0 +1,33 @@
{
"variants": {
"push_direction=all": {
"model": "extendedae_plus:block/mirror_pattern_provider"
},
"push_direction=down": {
"model": "extendedae_plus:block/mirror_pattern_provider_oriented",
"x": 180
},
"push_direction=east": {
"model": "extendedae_plus:block/mirror_pattern_provider_oriented",
"x": 90,
"y": 90
},
"push_direction=north": {
"model": "extendedae_plus:block/mirror_pattern_provider_oriented",
"x": 90
},
"push_direction=south": {
"model": "extendedae_plus:block/mirror_pattern_provider_oriented",
"x": 90,
"y": 180
},
"push_direction=up": {
"model": "extendedae_plus:block/mirror_pattern_provider_oriented"
},
"push_direction=west": {
"model": "extendedae_plus:block/mirror_pattern_provider_oriented",
"x": 90,
"y": 270
}
}
}

View File

@ -68,6 +68,7 @@
"block.extendedae_plus.256x_crafting_accelerator": "256x Crafting Accelerator",
"block.extendedae_plus.1024x_crafting_accelerator": "1024x Crafting Accelerator",
"block.extendedae_plus.network_pattern_controller": "Pattern Supplier State Controller",
"block.extendedae_plus.mirror_pattern_provider": "Mirror Pattern Provider",
"block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core",
"block.extendedae_plus.assembler_matrix_speed_plus": "Assembler Matrix Speed Core Plus",
"block.extendedae_plus.assembler_matrix_crafter_plus": "Assembler Matrix Crafter Core Plus",
@ -171,6 +172,14 @@
"item.extendedae_plus.channel_card.owner.player": "Owner: Player %s",
"item.extendedae_plus.channel_card.owner.bound": "Bound to: %s",
"item.extendedae_plus.channel_card.owner.cleared": "Owner binding cleared",
"item.extendedae_plus.mirror_pattern_provider": "Mirror Pattern Provider",
"item.extendedae_plus.mirror_pattern_binding_tool": "Mirror Pattern Binder",
"item.extendedae_plus.mirror_pattern_binding_tool.tip.select": "Sneak-right-click a standard pattern provider to record it",
"item.extendedae_plus.mirror_pattern_binding_tool.tip.bind": "Right-click a mirror pattern provider to bind it to the recorded master",
"item.extendedae_plus.mirror_pattern_binding_tool.tip.clear": "Sneak-right-click air to clear the recorded master",
"item.extendedae_plus.mirror_pattern_binding_tool.selected": "Recorded master provider: (%d, %d, %d)",
"item.extendedae_plus.mirror_pattern_binding_tool.dimension": "Dimension: %s",
"item.extendedae_plus.mirror_pattern_binding_tool.unset": "No master pattern provider recorded",
"item.extendedae_plus.virtual_crafting_card": "Virtual Crafting Card",
"item.extendedae_plus.virtual_crafting_card.tooltip_main": "Auto-completes crafting jobs once the current pattern is about to finish",
"item.extendedae_plus.virtual_crafting_card.tooltip_detail": "Install inside a Pattern Provider upgrade slot",
@ -197,6 +206,15 @@
"extendedae_plus.chat.pattern_provider.global_toggle_applied": "Global toggle applied to %s pattern providers",
"extendedae_plus.message.config_update_failed": "Mapping file update failed: %s",
"extendedae_plus.message.config_delete_failed": "Mapping file delete failed: %s",
"extendedae_plus.message.mirror_pattern_provider.bound": "Mirror pattern provider bound to master provider: (%d, %d, %d)",
"extendedae_plus.message.mirror_pattern_provider.following": "Mirror pattern provider is following master provider: (%d, %d, %d)",
"extendedae_plus.message.mirror_pattern_provider.missing_master": "No standard master pattern provider is bound. Use the binder to bind one manually.",
"extendedae_plus.message.mirror_pattern_provider.readonly": "Mirror pattern provider has no standalone UI and follows the master's state and direction.",
"extendedae_plus.message.mirror_binding_tool.selected": "Recorded master pattern provider: (%d, %d, %d)",
"extendedae_plus.message.mirror_binding_tool.no_selection": "Sneak-right-click a standard pattern provider first to record it.",
"extendedae_plus.message.mirror_binding_tool.bind_failed": "Binding failed: the recorded position is not a valid standard pattern provider.",
"extendedae_plus.message.mirror_binding_tool.only_normal_provider": "This mirror binder only supports the standard pattern provider, not the extended one.",
"extendedae_plus.message.mirror_binding_tool.cleared": "Cleared the master pattern provider recorded in the binding tool.",
"config.jade.plugin_extendedae_plus.wireless_transceiver_info": "Wireless Transceiver Info",
"config.jade.plugin_extendedae_plus.wt_frequency": "Show Frequency",

View File

@ -66,6 +66,7 @@
"block.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元",
"block.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元",
"block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器",
"block.extendedae_plus.mirror_pattern_provider": "镜像样板供应器",
"block.extendedae_plus.assembler_matrix_upload_core": "装配矩阵上传核心",
"block.extendedae_plus.assembler_matrix_speed_plus": "超级装配矩阵速度核心",
"block.extendedae_plus.assembler_matrix_crafter_plus": "超级装配矩阵合成核心",
@ -170,6 +171,14 @@
"item.extendedae_plus.channel_card.owner.player": "所有者:玩家 %s",
"item.extendedae_plus.channel_card.owner.bound": "已绑定至:%s",
"item.extendedae_plus.channel_card.owner.cleared": "已清除所有者绑定",
"item.extendedae_plus.mirror_pattern_provider": "镜像样板供应器",
"item.extendedae_plus.mirror_pattern_binding_tool": "镜像样板绑定器",
"item.extendedae_plus.mirror_pattern_binding_tool.tip.select": "潜行右键普通样板供应器:记录主供应器",
"item.extendedae_plus.mirror_pattern_binding_tool.tip.bind": "右键镜像样板供应器:绑定到当前记录的主供应器",
"item.extendedae_plus.mirror_pattern_binding_tool.tip.clear": "潜行右键空气:清空当前记录的主供应器",
"item.extendedae_plus.mirror_pattern_binding_tool.selected": "当前记录主供应器:(%d, %d, %d)",
"item.extendedae_plus.mirror_pattern_binding_tool.dimension": "维度:%s",
"item.extendedae_plus.mirror_pattern_binding_tool.unset": "当前未记录主样板供应器",
"item.extendedae_plus.virtual_crafting_card": "虚拟合成卡",
"item.extendedae_plus.virtual_crafting_card.tooltip_main": "当前样板即将完成时尝试终止剩余合成",
"item.extendedae_plus.virtual_crafting_card.tooltip_detail": "需安装在样板供应器的升级槽内",
@ -196,6 +205,15 @@
"extendedae_plus.chat.pattern_provider.global_toggle_applied": "全局切换已应用到 %s 个样板供应器",
"extendedae_plus.message.config_update_failed": "映射文件更新失败: %s",
"extendedae_plus.message.config_delete_failed": "映射文件删除失败: %s",
"extendedae_plus.message.mirror_pattern_provider.bound": "镜像样板供应器已绑定到主供应器:(%d, %d, %d)",
"extendedae_plus.message.mirror_pattern_provider.following": "镜像样板供应器当前跟随主供应器:(%d, %d, %d)",
"extendedae_plus.message.mirror_pattern_provider.missing_master": "未绑定普通主样板供应器,请使用绑定器手动绑定",
"extendedae_plus.message.mirror_pattern_provider.readonly": "镜像样板供应器没有独立界面,状态与朝向会跟随主供应器",
"extendedae_plus.message.mirror_binding_tool.selected": "已记录主样板供应器:(%d, %d, %d)",
"extendedae_plus.message.mirror_binding_tool.no_selection": "请先潜行右键一个普通样板供应器进行记录",
"extendedae_plus.message.mirror_binding_tool.bind_failed": "绑定失败:记录的位置不是可用的普通样板供应器",
"extendedae_plus.message.mirror_binding_tool.only_normal_provider": "当前镜像样板绑定器只支持普通样板供应器,不支持扩展样板供应器",
"extendedae_plus.message.mirror_binding_tool.cleared": "已清空绑定工具中记录的主样板供应器",
"config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息",
"config.jade.plugin_extendedae_plus.wt_frequency": "显示频率",

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_default"
}
}

View File

@ -0,0 +1,12 @@
{
"parent": "block/cube",
"textures": {
"particle": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_default",
"down": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_back",
"up": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_front",
"north": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_side",
"south": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_side",
"east": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_side",
"west": "extendedae_plus:block/mirror_pattern_provider/mirror_pattern_provider_side"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "extendedae_plus:item/mirror_pattern_binding_tool"
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "extendedae_plus:block/mirror_pattern_provider"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "extendedae_plus:mirror_pattern_provider"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View File

@ -2,6 +2,7 @@
"replace": false,
"values": [
"extendedae_plus:network_pattern_controller",
"extendedae_plus:mirror_pattern_provider",
"extendedae_plus:wireless_transceiver",
"extendedae_plus:assembler_matrix_upload_core",
"extendedae_plus:assembler_matrix_speed_plus",

View File

@ -1,6 +1,7 @@
{
"replace": false,
"values": [
"extendedae_plus:mirror_pattern_provider",
"extendedae_plus:wireless_transceiver",
"extendedae_plus:assembler_matrix_upload_core",
"extendedae_plus:assembler_matrix_speed_plus",