package appeng.container.implementations; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.inventory.container.SimpleNamedContainerProvider; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.World; import net.minecraftforge.fml.network.NetworkHooks; import appeng.api.config.SecurityPermissions; import appeng.api.features.IWirelessTermHandler; import appeng.api.implementations.guiobjects.IGuiItem; import appeng.api.implementations.guiobjects.IGuiItemObject; import appeng.api.parts.IPart; import appeng.api.parts.IPartHost; import appeng.container.AEBaseContainer; import appeng.container.ContainerLocator; import appeng.core.AELog; import appeng.core.Api; import appeng.helpers.ICustomNameObject; import appeng.helpers.WirelessTerminalGuiObject; import appeng.util.Platform; /** * Helper for containers that can be opened for a part or tile given * that either implements a given interface. * * @param */ public final class ContainerHelper { private final Class interfaceClass; private final ContainerFactory factory; private final SecurityPermissions requiredPermission; public ContainerHelper(ContainerFactory factory, Class interfaceClass) { this(factory, interfaceClass, null); } public ContainerHelper(ContainerFactory factory, Class interfaceClass, SecurityPermissions requiredPermission) { this.requiredPermission = requiredPermission; this.interfaceClass = interfaceClass; this.factory = factory; } /** * Opens a container that is based around a single tile entity. The tile * entity's position is encoded in the packet buffer. */ public C fromNetwork(int windowId, PlayerInventory inv, PacketBuffer packetBuf) { I host = getHostFromLocator(inv.player, ContainerLocator.read(packetBuf)); if (host != null) { return factory.create(windowId, inv, host); } return null; } public boolean open(PlayerEntity player, ContainerLocator locator) { if (!(player instanceof ServerPlayerEntity)) { // Cannot open containers on the client or for non-players // FIXME logging? return false; } I accessInterface = getHostFromLocator(player, locator); if (accessInterface == null) { return false; } if (!checkPermission(player, accessInterface)) { return false; } ITextComponent title = findContainerTitle(player.world, locator, accessInterface); INamedContainerProvider container = new SimpleNamedContainerProvider((wnd, p, pl) -> { C c = factory.create(wnd, p, accessInterface); // Set the original locator on the opened server-side container for it to more // easily remember how to re-open after being closed. c.setLocator(locator); return c; }, title); NetworkHooks.openGui((ServerPlayerEntity) player, container, locator::write); return true; } private ITextComponent findContainerTitle(World world, ContainerLocator locator, I accessInterface) { if (accessInterface instanceof ICustomNameObject) { ICustomNameObject customNameObject = (ICustomNameObject) accessInterface; if (customNameObject.hasCustomInventoryName()) { return customNameObject.getCustomInventoryName(); } } // Use block name at position // FIXME: this is not right, we'd need to check the part's item stack, or custom // naming interface impl // FIXME: Should move this up, because at this point, it's hard to know where // the terminal host came from (part or tile) if (locator.hasBlockPos()) { return new TranslationTextComponent( world.getBlockState(locator.getBlockPos()).getBlock().getTranslationKey()); } return new StringTextComponent("Unknown"); } private I getHostFromLocator(PlayerEntity player, ContainerLocator locator) { if (locator.hasItemIndex()) { return getHostFromPlayerInventory(player, locator); } if (!locator.hasBlockPos()) { return null; // No block was clicked } TileEntity tileEntity = player.world.getTileEntity(locator.getBlockPos()); // The tile entity itself can host a terminal (i.e. Chest!) if (interfaceClass.isInstance(tileEntity)) { return interfaceClass.cast(tileEntity); } if (!locator.hasSide()) { return null; } if (tileEntity instanceof IPartHost) { // But it could also be a part attached to the tile entity IPartHost partHost = (IPartHost) tileEntity; IPart part = partHost.getPart(locator.getSide()); if (part == null) { return null; } if (interfaceClass.isInstance(part)) { return interfaceClass.cast(part); } else { AELog.debug("Trying to open a container @ %s for a %s, but the container requires %s", locator, part.getClass(), interfaceClass); return null; } } else { // FIXME: Logging? Dont know how to obtain the terminal host return null; } } private I getHostFromPlayerInventory(PlayerEntity player, ContainerLocator locator) { ItemStack it = player.inventory.getStackInSlot(locator.getItemIndex()); if (it.isEmpty()) { AELog.debug("Cannot open container for player %s since they no longer hold the item in slot %d", player, locator.hasItemIndex()); return null; } if (it.getItem() instanceof IGuiItem) { IGuiItem guiItem = (IGuiItem) it.getItem(); // Optionally contains the block the item was used on to open the container BlockPos blockPos = locator.hasBlockPos() ? locator.getBlockPos() : null; IGuiItemObject guiObject = guiItem.getGuiObject(it, locator.getItemIndex(), player.world, blockPos); if (interfaceClass.isInstance(guiObject)) { return interfaceClass.cast(guiObject); } } if (interfaceClass.isAssignableFrom(WirelessTerminalGuiObject.class)) { final IWirelessTermHandler wh = Api.instance().registries().wireless().getWirelessTerminalHandler(it); if (wh != null) { return interfaceClass.cast(new WirelessTerminalGuiObject(wh, it, player, locator.getItemIndex())); } } return null; } @FunctionalInterface public interface ContainerFactory { C create(int windowId, PlayerInventory playerInv, I accessObj); } private boolean checkPermission(PlayerEntity player, Object accessInterface) { if (requiredPermission != null) { return Platform.checkPermissions(player, accessInterface, requiredPermission, true); } return true; } }