From b66aa4c4d3df2d75d596ff4f601f244566a6866e Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Fri, 12 Jul 2019 12:13:05 +0200 Subject: [PATCH] Schematicannon and Holograms - Uploaded Blueprints can be previewed by the owner - The Schematicannon prints the schematic from blocks in attached inventories --- .../java/com/simibubi/create/AllItems.java | 3 +- .../create/ClientSchematicLoader.java | 4 +- src/main/java/com/simibubi/create/Create.java | 2 + .../create/ServerSchematicLoader.java | 49 ++- .../create/block/SchematicTableContainer.java | 60 +++- .../block/SchematicTableTileEntity.java | 26 +- .../create/block/SchematicannonBlock.java | 29 +- .../block/SchematicannonTileEntity.java | 143 +++++++-- .../create/gui/SchematicTableScreen.java | 24 +- .../simibubi/create/item/ItemBlueprint.java | 102 +++++++ .../PacketSchematicTableContainer.java | 48 +++ .../simibubi/create/networking/Packets.java | 15 +- .../com/simibubi/create/schematic/Cuboid.java | 71 +++++ .../create/schematic/SchematicHologram.java | 210 +++++++++++++ .../create/schematic/SchematicWorld.java | 282 ++++++++++++++++++ 15 files changed, 1002 insertions(+), 66 deletions(-) create mode 100644 src/main/java/com/simibubi/create/item/ItemBlueprint.java create mode 100644 src/main/java/com/simibubi/create/networking/PacketSchematicTableContainer.java create mode 100644 src/main/java/com/simibubi/create/schematic/Cuboid.java create mode 100644 src/main/java/com/simibubi/create/schematic/SchematicHologram.java create mode 100644 src/main/java/com/simibubi/create/schematic/SchematicWorld.java diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java index b6ebfa2d1..f8412edc1 100644 --- a/src/main/java/com/simibubi/create/AllItems.java +++ b/src/main/java/com/simibubi/create/AllItems.java @@ -1,5 +1,6 @@ package com.simibubi.create; +import com.simibubi.create.item.ItemBlueprint; import com.simibubi.create.item.ItemWandSymmetry; import net.minecraft.item.Item; @@ -11,7 +12,7 @@ public enum AllItems { SYMMETRY_WAND(new ItemWandSymmetry(standardProperties())), EMPTY_BLUEPRINT(new Item(standardProperties().maxStackSize(1))), - BLUEPRINT(new Item(standardProperties().maxStackSize(1))); + BLUEPRINT(new ItemBlueprint(standardProperties())); public Item item; diff --git a/src/main/java/com/simibubi/create/ClientSchematicLoader.java b/src/main/java/com/simibubi/create/ClientSchematicLoader.java index 8e252e43c..541427e3a 100644 --- a/src/main/java/com/simibubi/create/ClientSchematicLoader.java +++ b/src/main/java/com/simibubi/create/ClientSchematicLoader.java @@ -66,7 +66,7 @@ public class ClientSchematicLoader { Create.logger.fatal("Missing Schematic file: " + path.toString()); return; } - + InputStream in; try { in = Files.newInputStream(path, StandardOpenOption.READ); @@ -108,7 +108,7 @@ public class ClientSchematicLoader { activeUploads.remove(schematic); } } - + public void refresh() { FilesHelper.createFolderIfMissing("schematics"); availableSchematics.clear(); diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java index 934de5951..6d43e6173 100644 --- a/src/main/java/com/simibubi/create/Create.java +++ b/src/main/java/com/simibubi/create/Create.java @@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.simibubi.create.networking.Packets; +import com.simibubi.create.schematic.SchematicHologram; import net.minecraft.block.Block; import net.minecraft.item.Item; @@ -50,6 +51,7 @@ public class Create { AllTileEntities.registerRenderers(); cSchematicLoader = new ClientSchematicLoader(); sSchematicLoader = new ServerSchematicLoader(); + new SchematicHologram(); // ScrollFixer.init(); } diff --git a/src/main/java/com/simibubi/create/ServerSchematicLoader.java b/src/main/java/com/simibubi/create/ServerSchematicLoader.java index 1f28622b4..28930408c 100644 --- a/src/main/java/com/simibubi/create/ServerSchematicLoader.java +++ b/src/main/java/com/simibubi/create/ServerSchematicLoader.java @@ -21,7 +21,7 @@ import net.minecraft.util.text.TextFormatting; public class ServerSchematicLoader { - private static final String PATH = "schematics/uploaded"; + public static final String PATH = "schematics/uploaded"; private Map activeDownloads; private Map activeTables; @@ -38,6 +38,9 @@ public class ServerSchematicLoader { FilesHelper.createFolderIfMissing(playerPath); + if (activeDownloads.containsKey(playerSchematicId)) + return; + try { Files.deleteIfExists(Paths.get(PATH, playerSchematicId)); OutputStream writer = Files.newOutputStream(Paths.get(PATH, playerSchematicId), @@ -45,6 +48,15 @@ public class ServerSchematicLoader { Create.logger.info("Receiving New Schematic: " + playerSchematicId); activeDownloads.put(playerSchematicId, writer); activeTables.put(playerSchematicId, dimensionPos); + + if (player.openContainer instanceof SchematicTableContainer) { + SchematicTableContainer c = (SchematicTableContainer) player.openContainer; + c.schematicUploading = schematic; + c.isUploading = true; + c.sendSchematicUpdate = true; + player.openContainer.detectAndSendChanges(); + } + } catch (IOException e) { e.printStackTrace(); } @@ -53,11 +65,14 @@ public class ServerSchematicLoader { public void handleWriteRequest(ServerPlayerEntity player, String schematic, byte[] data) { String playerSchematicId = player.getName().getFormattedText() + "/" + schematic; if (activeDownloads.containsKey(playerSchematicId)) { + try { activeDownloads.get(playerSchematicId).write(data); Create.logger.info("Writing to Schematic: " + playerSchematicId); } catch (IOException e) { e.printStackTrace(); + activeDownloads.remove(playerSchematicId); + activeTables.remove(playerSchematicId); } } } @@ -68,13 +83,27 @@ public class ServerSchematicLoader { if (activeDownloads.containsKey(playerSchematicId)) { try { activeDownloads.get(playerSchematicId).close(); + activeDownloads.remove(playerSchematicId); Create.logger.info("Finished receiving Schematic: " + playerSchematicId); DimensionPos dimpos = activeTables.remove(playerSchematicId); + + if (dimpos == null) + return; + BlockState blockState = dimpos.world.getBlockState(dimpos.pos); if (!AllBlocks.SCHEMATIC_TABLE.typeOf(blockState)) return; + if (player.openContainer instanceof SchematicTableContainer) { + SchematicTableContainer c = (SchematicTableContainer) player.openContainer; + c.isUploading = false; + c.schematicUploading = null; + c.progress = 0; + c.sendSchematicUpdate = true; + c.detectAndSendChanges(); + } + SchematicTableTileEntity tileEntity = (SchematicTableTileEntity) dimpos.world.getTileEntity(dimpos.pos); if (tileEntity.inputStack.isEmpty()) return; @@ -82,15 +111,19 @@ public class ServerSchematicLoader { return; tileEntity.inputStack = ItemStack.EMPTY; - tileEntity.outputStack = new ItemStack(AllItems.BLUEPRINT.get()); - tileEntity.outputStack - .setDisplayName(new StringTextComponent(TextFormatting.RESET + "" + TextFormatting.WHITE - + "Blueprint (" + TextFormatting.GOLD + schematic + TextFormatting.WHITE + ")")); - tileEntity.markDirty(); + ItemStack blueprint = new ItemStack(AllItems.BLUEPRINT.get()); + blueprint.setDisplayName(new StringTextComponent(TextFormatting.RESET + "" + TextFormatting.WHITE + + "Blueprint (" + TextFormatting.GOLD + schematic + TextFormatting.WHITE + ")")); + blueprint.getTag().putString("Owner", player.getName().getFormattedText()); + blueprint.getTag().putString("File", schematic); + + tileEntity.outputStack = blueprint; + dimpos.world.notifyBlockUpdate(dimpos.pos, blockState, blockState, 3); + if (player.openContainer instanceof SchematicTableContainer) { - ((SchematicTableContainer) player.openContainer).updateContent(); - player.openContainer.detectAndSendChanges(); + SchematicTableContainer c = (SchematicTableContainer) player.openContainer; + c.updateContent(); } } catch (IOException e) { diff --git a/src/main/java/com/simibubi/create/block/SchematicTableContainer.java b/src/main/java/com/simibubi/create/block/SchematicTableContainer.java index 7a01ba074..7d07a1e51 100644 --- a/src/main/java/com/simibubi/create/block/SchematicTableContainer.java +++ b/src/main/java/com/simibubi/create/block/SchematicTableContainer.java @@ -2,14 +2,20 @@ package com.simibubi.create.block; import com.simibubi.create.AllContainers; import com.simibubi.create.AllItems; +import com.simibubi.create.networking.PacketSchematicTableContainer; +import com.simibubi.create.networking.Packets; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.Slot; import net.minecraft.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.network.PacketDistributor; public class SchematicTableContainer extends Container { @@ -23,13 +29,20 @@ public class SchematicTableContainer extends Container { private SchematicTableTileEntity te; private Slot inputSlot; private Slot outputSlot; + private PlayerEntity player; + + public String schematicUploading; + public boolean isUploading; + public float progress; + public boolean sendSchematicUpdate; public SchematicTableContainer(int id, PlayerInventory inv) { this(id, inv, null); } - + public SchematicTableContainer(int id, PlayerInventory inv, SchematicTableTileEntity te) { super(AllContainers.SchematicTable.type, id); + this.player = inv.player; this.te = te; inputSlot = new Slot(tableInventory, 0, 31, 15) { @@ -51,6 +64,10 @@ public class SchematicTableContainer extends Container { updateContent(); + if (te != null) { + this.addListener(te); + } + // player Slots tableInventory.openInventory(inv.player); for (int l = 0; l < 3; ++l) { @@ -63,6 +80,12 @@ public class SchematicTableContainer extends Container { this.addSlot(new Slot(inv, i1, -8 + i1 * 18, 135)); } } + + @Override + public void detectAndSendChanges() { + super.detectAndSendChanges(); + sendSchematicInfo(); + } public boolean canWrite() { return inputSlot.getHasStack() && !outputSlot.getHasStack(); @@ -98,14 +121,41 @@ public class SchematicTableContainer extends Container { return ItemStack.EMPTY; } - + public void updateContent() { if (te != null) { inputSlot.putStack(te.inputStack); outputSlot.putStack(te.outputStack); + schematicUploading = te.uploadingSchematic; + progress = te.uploadingProgress; + sendSchematicUpdate = true; } } - + + public void sendSchematicInfo() { + if (player instanceof ServerPlayerEntity) { + if (sendSchematicUpdate) { + Packets.channel.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), + new PacketSchematicTableContainer(schematicUploading, progress)); + sendSchematicUpdate = false; + } + } + } + + @OnlyIn(Dist.CLIENT) + public void receiveSchematicInfo(String schematic, float progress) { + if (schematic.isEmpty()) { + this.schematicUploading = null; + this.isUploading = false; + this.progress = 0; + return; + } + + this.isUploading = true; + this.schematicUploading = schematic; + this.progress = .5f; + } + @Override public void onContainerClosed(PlayerEntity playerIn) { if (te != null) { @@ -113,10 +163,10 @@ public class SchematicTableContainer extends Container { te.outputStack = outputSlot.getStack(); te.markDirty(); } - + super.onContainerClosed(playerIn); } - + public SchematicTableTileEntity getTileEntity() { return te; } diff --git a/src/main/java/com/simibubi/create/block/SchematicTableTileEntity.java b/src/main/java/com/simibubi/create/block/SchematicTableTileEntity.java index a6784191a..fd7efde91 100644 --- a/src/main/java/com/simibubi/create/block/SchematicTableTileEntity.java +++ b/src/main/java/com/simibubi/create/block/SchematicTableTileEntity.java @@ -7,6 +7,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.ItemStackHelper; import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.IContainerListener; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; @@ -16,11 +17,14 @@ import net.minecraft.util.NonNullList; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.StringTextComponent; -public class SchematicTableTileEntity extends TileEntitySynced implements ITickableTileEntity, INamedContainerProvider { +public class SchematicTableTileEntity extends TileEntitySynced implements ITickableTileEntity, INamedContainerProvider, IContainerListener { public ItemStack inputStack; public ItemStack outputStack; + public String uploadingSchematic; + public float uploadingProgress; + public SchematicTableTileEntity() { this(AllTileEntities.SchematicTable.type); } @@ -69,5 +73,25 @@ public class SchematicTableTileEntity extends TileEntitySynced implements ITicka public ITextComponent getDisplayName() { return new StringTextComponent(getType().getRegistryName().toString()); } + + @Override + public void sendAllContents(Container containerToSend, NonNullList itemsList) { + inputStack = itemsList.get(0); + outputStack = itemsList.get(1); + } + + @Override + public void sendSlotContents(Container containerToSend, int slotInd, ItemStack stack) { + if (slotInd == 0) { + inputStack = stack; + } + if (slotInd == 1) { + outputStack = stack; + } + } + + @Override + public void sendWindowProperty(Container containerIn, int varToUpdate, int newValue) { + } } diff --git a/src/main/java/com/simibubi/create/block/SchematicannonBlock.java b/src/main/java/com/simibubi/create/block/SchematicannonBlock.java index 608b7e180..d15fe2fcd 100644 --- a/src/main/java/com/simibubi/create/block/SchematicannonBlock.java +++ b/src/main/java/com/simibubi/create/block/SchematicannonBlock.java @@ -3,14 +3,12 @@ package com.simibubi.create.block; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.Hand; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.IBlockReader; -import net.minecraft.world.World; +import net.minecraft.world.IWorld; +import net.minecraft.world.IWorldReader; public class SchematicannonBlock extends Block { @@ -29,17 +27,16 @@ public class SchematicannonBlock extends Block { } @Override - public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, - BlockRayTraceResult hit) { - TileEntity tileEntity = worldIn.getTileEntity(pos); - if (worldIn.isRemote) - return true; - - if (tileEntity == null) - return false; - - player.sendMessage(new StringTextComponent("" + ((SchematicannonTileEntity) tileEntity).getTest())); - return true; + public BlockState updatePostPlacement(BlockState stateIn, Direction facing, BlockState facingState, IWorld worldIn, + BlockPos currentPos, BlockPos facingPos) { + ((SchematicannonTileEntity) worldIn.getTileEntity(currentPos)).findInventories(); + return stateIn; + } + + @Override + public void onNeighborChange(BlockState state, IWorldReader world, BlockPos pos, BlockPos neighbor) { + ((SchematicannonTileEntity) world.getTileEntity(pos)).findInventories(); + super.onNeighborChange(state, world, pos, neighbor); } } diff --git a/src/main/java/com/simibubi/create/block/SchematicannonTileEntity.java b/src/main/java/com/simibubi/create/block/SchematicannonTileEntity.java index 9c3077c46..7dcf46dc7 100644 --- a/src/main/java/com/simibubi/create/block/SchematicannonTileEntity.java +++ b/src/main/java/com/simibubi/create/block/SchematicannonTileEntity.java @@ -1,48 +1,155 @@ package com.simibubi.create.block; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.io.IOUtils; + import com.simibubi.create.AllTileEntities; +import com.simibubi.create.ServerSchematicLoader; +import com.simibubi.create.schematic.Cuboid; +import com.simibubi.create.schematic.SchematicWorld; import com.simibubi.create.utility.TileEntitySynced; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.tileentity.ITickableTileEntity; +import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.gen.feature.template.PlacementSettings; +import net.minecraft.world.gen.feature.template.Template; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.IItemHandler; public class SchematicannonTileEntity extends TileEntitySynced implements ITickableTileEntity { - private int test = 0; - + public static final int PLACEMENT_DELAY = 2; + + private SchematicWorld reader; + private BlockPos currentPos; + public BlockPos anchor; + public String schematicToPrint; + public boolean missingBlock; + + public List attachedInventories; + + private int cooldown; + public SchematicannonTileEntity() { - super(AllTileEntities.Schematicannon.type); + this(AllTileEntities.Schematicannon.type); } - + public SchematicannonTileEntity(TileEntityType tileEntityTypeIn) { super(tileEntityTypeIn); + attachedInventories = new LinkedList<>(); } - + + public void findInventories() { + for (Direction facing : Direction.values()) { + TileEntity tileEntity = world.getTileEntity(pos.offset(facing)); + if (tileEntity != null) { + LazyOptional capability = tileEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite()); + if (capability.isPresent()) { + attachedInventories.add(capability.orElse(null)); + } + } + } + } + @Override public void read(CompoundNBT compound) { super.read(compound); - test = compound.getInt("Test"); } - + @Override public CompoundNBT write(CompoundNBT compound) { - compound.putInt("Test", test); return super.write(compound); } - - public int getTest() { - return test; - } - - public void setTest(int test) { - this.test = test; - } @Override public void tick() { - test++; + if (world.isRemote) + return; + if (schematicToPrint == null) + return; + if (cooldown-- > 0) + return; + cooldown = PLACEMENT_DELAY; + + if (reader == null) { + currentPos = BlockPos.ZERO; + currentPos = currentPos.offset(Direction.WEST); + + String filepath = ServerSchematicLoader.PATH + "/" + schematicToPrint; + Template activeTemplate = new Template(); + + InputStream stream = null; + try { + stream = Files.newInputStream(Paths.get(filepath), StandardOpenOption.READ); + CompoundNBT nbt = CompressedStreamTools.readCompressed(stream); + activeTemplate.read(nbt); + reader = new SchematicWorld(new HashMap<>(), new Cuboid(BlockPos.ZERO, 0, 0, 0), anchor); + activeTemplate.addBlocksToWorld(reader, anchor, new PlacementSettings()); + + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (stream != null) + IOUtils.closeQuietly(stream); + } + return; + } + + BlockPos size = reader.getBounds().getSize(); + BlockState state; + do { + if (!missingBlock) + currentPos = currentPos.offset(Direction.EAST); + if (currentPos.getX() > size.getX()) { + currentPos = new BlockPos(0, currentPos.getY(), currentPos.getZ() + 1); + } + if (currentPos.getZ() > size.getZ()) { + currentPos = new BlockPos(currentPos.getX(), currentPos.getY() + 1, 0); + } + if (currentPos.getY() > size.getY()) { + schematicToPrint = null; + currentPos = null; + anchor = null; + reader = null; + missingBlock = false; + return; + } + state = reader.getBlockState(anchor.add(currentPos)); + } while (state.getBlock() == Blocks.AIR); + + missingBlock = true; + ItemStack requiredItem = new ItemStack(BlockItem.BLOCK_TO_ITEM.getOrDefault(state.getBlock(), Items.AIR)); + for (IItemHandler iItemHandler : attachedInventories) { + for (int slot = 0; slot < iItemHandler.getSlots(); slot++) { + ItemStack stackInSlot = iItemHandler.getStackInSlot(slot); + if (!stackInSlot.isItemEqual(requiredItem)) + continue; + iItemHandler.extractItem(slot, 1, false); + world.setBlockState(currentPos.add(anchor), state); + missingBlock = false; + return; + } + } + } - } diff --git a/src/main/java/com/simibubi/create/gui/SchematicTableScreen.java b/src/main/java/com/simibubi/create/gui/SchematicTableScreen.java index ab60d10ca..30332a0f2 100644 --- a/src/main/java/com/simibubi/create/gui/SchematicTableScreen.java +++ b/src/main/java/com/simibubi/create/gui/SchematicTableScreen.java @@ -25,8 +25,6 @@ public class SchematicTableScreen extends ContainerScreen availableSchematics = Create.cSchematicLoader.getAvailableSchematics(); lastProgress = progress = 0; - uploadingSchematic = availableSchematics.get(schematics.getState()); - isUploading = true; - Create.cSchematicLoader.startNewUpload(uploadingSchematic); + String schematic = availableSchematics.get(schematics.getState()); + Create.cSchematicLoader.startNewUpload(schematic); } return super.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_); diff --git a/src/main/java/com/simibubi/create/item/ItemBlueprint.java b/src/main/java/com/simibubi/create/item/ItemBlueprint.java new file mode 100644 index 000000000..947104667 --- /dev/null +++ b/src/main/java/com/simibubi/create/item/ItemBlueprint.java @@ -0,0 +1,102 @@ +package com.simibubi.create.item; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +import org.apache.commons.io.IOUtils; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.block.SchematicannonTileEntity; +import com.simibubi.create.schematic.SchematicHologram; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.EquipmentSlotType; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUseContext; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTUtil; +import net.minecraft.util.ActionResult; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.template.Template; + +public class ItemBlueprint extends Item { + + public ItemBlueprint(Properties properties) { + super(properties.maxStackSize(1)); + } + + @Override + public ActionResultType onItemUse(ItemUseContext context) { + + World world = context.getWorld(); + + CompoundNBT tag = context.getItem().getTag(); + if (tag.contains("File")) { + + BlockPos pos = context.getPos(); + BlockState blockState = world.getBlockState(pos); + if (AllBlocks.SCHEMATICANNON.typeOf(blockState)) { + if (world.isRemote) { + SchematicHologram.reset(); + return ActionResultType.SUCCESS; + } + if (!tag.contains("Anchor")) + return ActionResultType.FAIL; + + SchematicannonTileEntity te = (SchematicannonTileEntity) world.getTileEntity(pos); + te.schematicToPrint = tag.getString("Owner") + "/" + tag.getString("File"); + te.anchor = NBTUtil.readBlockPos(tag.getCompound("Anchor")); + context.getPlayer().setItemStackToSlot(EquipmentSlotType.MAINHAND, ItemStack.EMPTY); + return ActionResultType.SUCCESS; + } + + tag.put("Anchor", NBTUtil.writeBlockPos(pos.offset(context.getFace()))); + + if (!world.isRemote) { + return ActionResultType.SUCCESS; + } + + if (!(context.getPlayer().getName().getFormattedText().equals(tag.getString("Owner")))) { + context.getPlayer() + .sendStatusMessage(new StringTextComponent("You are not the Owner of this Schematic."), true); + } + + String filepath = "schematics/" + tag.getString("File"); + Template t = new Template(); + + InputStream stream = null; + try { + stream = Files.newInputStream(Paths.get(filepath), StandardOpenOption.READ); + CompoundNBT nbt = CompressedStreamTools.readCompressed(stream); + t.read(nbt); + new SchematicHologram().startHologram(t, pos.offset(context.getFace())); + + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (stream != null) + IOUtils.closeQuietly(stream); + } + + } + + context.getPlayer().getCooldownTracker().setCooldown(this, 10); + return ActionResultType.SUCCESS; + } + + @Override + public ActionResult onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) { + return super.onItemRightClick(worldIn, playerIn, handIn); + } + +} diff --git a/src/main/java/com/simibubi/create/networking/PacketSchematicTableContainer.java b/src/main/java/com/simibubi/create/networking/PacketSchematicTableContainer.java new file mode 100644 index 000000000..bee33fe95 --- /dev/null +++ b/src/main/java/com/simibubi/create/networking/PacketSchematicTableContainer.java @@ -0,0 +1,48 @@ +package com.simibubi.create.networking; + +import java.util.function.Supplier; + +import com.simibubi.create.block.SchematicTableContainer; + +import net.minecraft.client.Minecraft; +import net.minecraft.inventory.container.Container; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.network.NetworkEvent.Context; + +public class PacketSchematicTableContainer { + + public String schematic; + public float progress; + + public PacketSchematicTableContainer(String schematicToUpload, float progress) { + this.schematic = schematicToUpload; + if (this.schematic == null) + this.schematic = ""; + this.progress = progress; + } + + public PacketSchematicTableContainer(PacketBuffer buffer) { + this.schematic = buffer.readString(); + this.progress = buffer.readFloat(); + } + + public void toBytes(PacketBuffer buffer) { + buffer.writeString(schematic); + buffer.writeFloat(progress); + } + + @OnlyIn(Dist.CLIENT) + public void handle(Supplier context) { + context.get().enqueueWork(() -> { + Container c = Minecraft.getInstance().player.openContainer; + if (c != null && c instanceof SchematicTableContainer) { + ((SchematicTableContainer) c).receiveSchematicInfo(schematic, progress); + } + + }); + + } + +} diff --git a/src/main/java/com/simibubi/create/networking/Packets.java b/src/main/java/com/simibubi/create/networking/Packets.java index 233a9dda5..1993307c3 100644 --- a/src/main/java/com/simibubi/create/networking/Packets.java +++ b/src/main/java/com/simibubi/create/networking/Packets.java @@ -13,16 +13,17 @@ public class Packets { public static final SimpleChannel channel = NetworkRegistry.newSimpleChannel( new ResourceLocation(Create.ID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); - + public static void registerPackets() { int i = 0; - channel.registerMessage(i++, PacketNbt.class, PacketNbt::toBytes, PacketNbt::new, - PacketNbt::handle); - channel.registerMessage(i++, PacketSchematicUpload.class, PacketSchematicUpload::toBytes, PacketSchematicUpload::new, - PacketSchematicUpload::handle); - channel.registerMessage(i++, PacketSymmetryEffect.class, PacketSymmetryEffect::toBytes, PacketSymmetryEffect::new, - PacketSymmetryEffect::handle); + channel.registerMessage(i++, PacketNbt.class, PacketNbt::toBytes, PacketNbt::new, PacketNbt::handle); + channel.registerMessage(i++, PacketSchematicTableContainer.class, PacketSchematicTableContainer::toBytes, + PacketSchematicTableContainer::new, PacketSchematicTableContainer::handle); + channel.registerMessage(i++, PacketSchematicUpload.class, PacketSchematicUpload::toBytes, + PacketSchematicUpload::new, PacketSchematicUpload::handle); + channel.registerMessage(i++, PacketSymmetryEffect.class, PacketSymmetryEffect::toBytes, + PacketSymmetryEffect::new, PacketSymmetryEffect::handle); } } diff --git a/src/main/java/com/simibubi/create/schematic/Cuboid.java b/src/main/java/com/simibubi/create/schematic/Cuboid.java new file mode 100644 index 000000000..7530aa0f5 --- /dev/null +++ b/src/main/java/com/simibubi/create/schematic/Cuboid.java @@ -0,0 +1,71 @@ +package com.simibubi.create.schematic; + +import net.minecraft.util.math.BlockPos; + +public class Cuboid { + + public int x; + public int y; + public int z; + public int width; + public int height; + public int length; + + public Cuboid(BlockPos origin, BlockPos size) { + this(origin, size.getX(), size.getY(), size.getZ()); + } + + public Cuboid(BlockPos origin, int width, int height, int length) { + this.x = origin.getX() + ((width < 0) ? width : 0); + this.y = origin.getY() + ((height < 0) ? height : 0); + this.z = origin.getZ() + ((length < 0) ? length : 0); + this.width = Math.abs(width); + this.height = Math.abs(height); + this.length = Math.abs(length); + } + + public BlockPos getOrigin() { + return new BlockPos(x, y, z); + } + + public BlockPos getSize() { + return new BlockPos(width, height, length); + } + + public Cuboid clone() { + return new Cuboid(new BlockPos(x, y, z), width, height, length); + } + + public void move(int x, int y, int z) { + this.x += x; + this.y += y; + this.z += z; + } + + public void centerHorizontallyOn(BlockPos pos) { + x = pos.getX() - (width / 2); + y = pos.getY(); + z = pos.getZ() - (length / 2); + } + + public boolean intersects(Cuboid other) { + return !(other.x >= x + width || other.z >= z + length || other.x + other.width <= x + || other.z + other.length <= z); + } + + public boolean contains(BlockPos pos) { + return (pos.getX() >= x && pos.getX() < x + width) && (pos.getY() >= y && pos.getY() < y + height) + && (pos.getZ() >= z && pos.getZ() < z + length); + } + + public BlockPos getCenter() { + return getOrigin().add(width / 2, height / 2, length / 2); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Cuboid && ((Cuboid) obj).getOrigin().equals(getOrigin()) + && ((Cuboid) obj).getSize().equals(getSize()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/schematic/SchematicHologram.java b/src/main/java/com/simibubi/create/schematic/SchematicHologram.java new file mode 100644 index 000000000..8a5d7fb3d --- /dev/null +++ b/src/main/java/com/simibubi/create/schematic/SchematicHologram.java @@ -0,0 +1,210 @@ +package com.simibubi.create.schematic; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import org.lwjgl.opengl.GL11; + +import com.mojang.blaze3d.platform.GlStateManager; + +import net.minecraft.block.BlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ActiveRenderInfo; +import net.minecraft.client.renderer.BlockRendererDispatcher; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.RegionRenderCacheBuilder; +import net.minecraft.client.renderer.texture.AtlasTexture; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.client.renderer.vertex.VertexFormatElement.Usage; +import net.minecraft.entity.Entity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.gen.feature.template.PlacementSettings; +import net.minecraft.world.gen.feature.template.Template; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.event.RenderWorldLastEvent; +import net.minecraftforge.client.model.data.EmptyModelData; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent; + +@EventBusSubscriber(Dist.CLIENT) +public class SchematicHologram { + + // These buffers are large enough for an entire chunk, consider using + // smaller buffers + private static final RegionRenderCacheBuilder bufferCache = new RegionRenderCacheBuilder(); + private static final boolean[] usedBlockRenderLayers = new boolean[BlockRenderLayer.values().length]; + private static final boolean[] startedBufferBuilders = new boolean[BlockRenderLayer.values().length]; + + private static SchematicHologram instance; + private boolean active; + private boolean changed; + private SchematicWorld schematic; + private BlockPos anchor; + + public SchematicHologram() { + instance = this; + changed = false; + } + + public void startHologram(Template schematic, BlockPos anchor) { + this.schematic = new SchematicWorld(new HashMap<>(), new Cuboid(BlockPos.ZERO, BlockPos.ZERO), anchor); + this.anchor = anchor; + schematic.addBlocksToWorld(this.schematic, anchor, new PlacementSettings()); + active = true; + changed = true; + } + + public static SchematicHologram getInstance() { + return instance; + } + +// public static void display(Schematic schematic) { +// instance = new SchematicHologram(); +// instance.startHologram(schematic); +// } + + public static void reset() { + instance = null; + } + + public void schematicChanged() { + changed = true; + } + + @SubscribeEvent + public static void onClientTickEvent(final ClientTickEvent event) { + if (instance != null && instance.active) { + final Minecraft minecraft = Minecraft.getInstance(); + if (event.phase != TickEvent.Phase.END) + return; + if (minecraft.world == null) + return; + if (minecraft.player == null) + return; + if (instance.changed) { + redraw(minecraft); + instance.changed = false; + } + } + } + + private static void redraw(final Minecraft minecraft) { + Arrays.fill(usedBlockRenderLayers, false); + Arrays.fill(startedBufferBuilders, false); + + final SchematicWorld blockAccess = instance.schematic; + final BlockRendererDispatcher blockRendererDispatcher = minecraft.getBlockRendererDispatcher(); + + List blockstates = new LinkedList<>(); + + for (BlockPos localPos : BlockPos.getAllInBoxMutable(blockAccess.getBounds().getOrigin(), + blockAccess.getBounds().getOrigin().add(blockAccess.getBounds().getSize()))) { + BlockPos pos = localPos.add(instance.anchor); + final BlockState state = blockAccess.getBlockState(pos); + for (BlockRenderLayer blockRenderLayer : BlockRenderLayer.values()) { + if (!state.getBlock().canRenderInLayer(state, blockRenderLayer)) { + continue; + } + ForgeHooksClient.setRenderLayer(blockRenderLayer); + final int blockRenderLayerId = blockRenderLayer.ordinal(); + final BufferBuilder bufferBuilder = bufferCache.getBuilder(blockRenderLayerId); + if (!startedBufferBuilders[blockRenderLayerId]) { + startedBufferBuilders[blockRenderLayerId] = true; + // Copied from RenderChunk + { + bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); + } + } + // OptiFine Shaders compatibility + // if (Config.isShaders()) SVertexBuilder.pushEntity(state, pos, + // blockAccess, bufferBuilder); + usedBlockRenderLayers[blockRenderLayerId] |= blockRendererDispatcher.renderBlock(state, pos, + blockAccess, bufferBuilder, minecraft.world.rand, EmptyModelData.INSTANCE); + blockstates.add(state); + // if (Config.isShaders()) + // SVertexBuilder.popEntity(bufferBuilder); + } + ForgeHooksClient.setRenderLayer(null); + } + + // finishDrawing + for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) { + if (!startedBufferBuilders[blockRenderLayerId]) { + continue; + } + bufferCache.getBuilder(blockRenderLayerId).finishDrawing(); + } + } + + @SubscribeEvent + public static void onRenderWorldLastEvent(final RenderWorldLastEvent event) { + if (instance != null && instance.active) { + final Entity entity = Minecraft.getInstance().getRenderViewEntity(); + + if (entity == null) { + return; + } + + ActiveRenderInfo renderInfo = Minecraft.getInstance().gameRenderer.getActiveRenderInfo(); + Vec3d view = renderInfo.getProjectedView(); + double renderPosX = view.x; + double renderPosY = view.y; + double renderPosZ = view.z; + + GlStateManager.enableAlphaTest(); + GlStateManager.enableBlend(); + Minecraft.getInstance().getTextureManager().bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE); + + for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) { + if (!usedBlockRenderLayers[blockRenderLayerId]) { + continue; + } + final BufferBuilder bufferBuilder = bufferCache.getBuilder(blockRenderLayerId); + GlStateManager.pushMatrix(); + GlStateManager.translated(-renderPosX, -renderPosY, -renderPosZ); + drawBuffer(bufferBuilder); + GlStateManager.popMatrix(); + } + GlStateManager.disableAlphaTest(); + GlStateManager.disableBlend(); + } + } + + // Coppied from the Tesselator's vboUploader - Draw everything but don't + // reset the buffer + private static void drawBuffer(final BufferBuilder bufferBuilder) { + if (bufferBuilder.getVertexCount() > 0) { + + VertexFormat vertexformat = bufferBuilder.getVertexFormat(); + int size = vertexformat.getSize(); + ByteBuffer bytebuffer = bufferBuilder.getByteBuffer(); + List list = vertexformat.getElements(); + + for (int index = 0; index < list.size(); ++index) { + VertexFormatElement vertexformatelement = list.get(index); + Usage usage = vertexformatelement.getUsage(); + bytebuffer.position(vertexformat.getOffset(index)); + usage.preDraw(vertexformat, index, size, bytebuffer); + } + + GlStateManager.drawArrays(bufferBuilder.getDrawMode(), 0, bufferBuilder.getVertexCount()); + + for (int index = 0; index < list.size(); ++index) { + VertexFormatElement vertexformatelement = list.get(index); + Usage usage = vertexformatelement.getUsage(); + usage.postDraw(vertexformat, index, size, bytebuffer); + } + } + } + +} diff --git a/src/main/java/com/simibubi/create/schematic/SchematicWorld.java b/src/main/java/com/simibubi/create/schematic/SchematicWorld.java new file mode 100644 index 000000000..2f08fdef4 --- /dev/null +++ b/src/main/java/com/simibubi/create/schematic/SchematicWorld.java @@ -0,0 +1,282 @@ +package com.simibubi.create.schematic; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.function.Predicate; + +import com.google.common.collect.ImmutableMap; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.Fluid; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.fluid.IFluidState; +import net.minecraft.particles.IParticleData; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.EmptyTickList; +import net.minecraft.world.ITickList; +import net.minecraft.world.IWorld; +import net.minecraft.world.LightType; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.Biomes; +import net.minecraft.world.border.WorldBorder; +import net.minecraft.world.chunk.AbstractChunkProvider; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.dimension.Dimension; +import net.minecraft.world.gen.Heightmap.Type; +import net.minecraft.world.storage.WorldInfo; + +public class SchematicWorld implements IWorld { + + private Map blocks; + private Cuboid bounds; + private BlockPos anchor; + + public SchematicWorld(Map blocks, Cuboid bounds, BlockPos anchor) { + this.blocks = blocks; + this.setBounds(bounds); + this.anchor = anchor; + updateBlockstates(); + } + + private void updateBlockstates() { + Set keySet = new HashSet<>(blocks.keySet()); + keySet.forEach(pos -> { + BlockState blockState = blocks.get(pos); + if (blockState == null) + return; + blockState.updateNeighbors(this, pos.add(anchor), 16); + }); + } + + public Set getAllPositions() { + return blocks.keySet(); + } + + @Override + public TileEntity getTileEntity(BlockPos pos) { + return null; + } + + @Override + public BlockState getBlockState(BlockPos globalPos) { + BlockPos pos = globalPos.subtract(anchor); + if (getBounds().contains(pos) && blocks.containsKey(pos)) { + return blocks.get(pos); + } else { + return Blocks.AIR.getDefaultState(); + } + } + + public Map getBlockMap() { + return blocks; + } + + @Override + public IFluidState getFluidState(BlockPos pos) { + return new FluidState(Fluids.EMPTY, ImmutableMap.of()); + } + + @Override + public Biome getBiome(BlockPos pos) { + return Biomes.THE_VOID; + } + + @Override + public int getLightFor(LightType type, BlockPos pos) { + return 10; + } + + @Override + public List getEntitiesInAABBexcluding(Entity arg0, AxisAlignedBB arg1, Predicate arg2) { + return Collections.emptyList(); + } + + @Override + public List getEntitiesWithinAABB(Class arg0, AxisAlignedBB arg1, + Predicate arg2) { + return Collections.emptyList(); + } + + @Override + public List getPlayers() { + return Collections.emptyList(); + } + + @Override + public int getLightSubtracted(BlockPos pos, int amount) { + return 0; + } + + @Override + public IChunk getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull) { + return null; + } + + @Override + public BlockPos getHeight(Type heightmapType, BlockPos pos) { + return BlockPos.ZERO; + } + + @Override + public int getHeight(Type heightmapType, int x, int z) { + return 0; + } + + @Override + public int getSkylightSubtracted() { + return 0; + } + + @Override + public WorldBorder getWorldBorder() { + return null; + } + + @Override + public boolean isRemote() { + return false; + } + + @Override + public int getSeaLevel() { + return 0; + } + + @Override + public Dimension getDimension() { + return null; + } + + @Override + public boolean hasBlockState(BlockPos pos, Predicate predicate) { + return predicate.test(getBlockState(pos)); + } + + @Override + public boolean destroyBlock(BlockPos arg0, boolean arg1) { + return setBlockState(arg0, Blocks.AIR.getDefaultState(), 3); + } + + @Override + public boolean removeBlock(BlockPos arg0, boolean arg1) { + return setBlockState(arg0, Blocks.AIR.getDefaultState(), 3); + } + + @Override + public boolean setBlockState(BlockPos pos, BlockState arg1, int arg2) { + pos = pos.subtract(anchor); + if (pos.getX() < bounds.x) { + bounds.width += bounds.x - pos.getX(); + bounds.x = pos.getX(); + } + if (pos.getY() < bounds.y) { + bounds.height += bounds.y - pos.getY(); + bounds.y = pos.getY(); + } + if (pos.getZ() < bounds.z) { + bounds.length += bounds.z - pos.getZ(); + bounds.z = pos.getZ(); + } + BlockPos boundsMax = bounds.getOrigin().add(bounds.getSize()); + if (boundsMax.getX() <= pos.getX()) { + bounds.width += pos.getX() - boundsMax.getX() + 1; + } + if (boundsMax.getY() <= pos.getY()) { + bounds.height += pos.getY() - boundsMax.getY() + 1; + } + if (boundsMax.getZ() <= pos.getZ()) { + bounds.length += pos.getZ() - boundsMax.getZ() + 1; + } + + blocks.put(pos, arg1); + return true; + } + + @Override + public long getSeed() { + return 0; + } + + @Override + public ITickList getPendingBlockTicks() { + return EmptyTickList.get(); + } + + @Override + public ITickList getPendingFluidTicks() { + return EmptyTickList.get(); + } + + @Override + public World getWorld() { + return null; + } + + @Override + public WorldInfo getWorldInfo() { + return null; + } + + @Override + public DifficultyInstance getDifficultyForLocation(BlockPos pos) { + return null; + } + + @Override + public AbstractChunkProvider getChunkProvider() { + return null; + } + + @Override + public Random getRandom() { + return new Random(); + } + + @Override + public void notifyNeighbors(BlockPos pos, Block blockIn) { + } + + @Override + public BlockPos getSpawnPoint() { + return null; + } + + @Override + public void playSound(PlayerEntity player, BlockPos pos, SoundEvent soundIn, SoundCategory category, float volume, + float pitch) { + } + + @Override + public void addParticle(IParticleData particleData, double x, double y, double z, double xSpeed, double ySpeed, + double zSpeed) { + } + + @Override + public void playEvent(PlayerEntity player, int type, BlockPos pos, int data) { + } + + public Cuboid getBounds() { + return bounds; + } + + public void setBounds(Cuboid bounds) { + this.bounds = bounds; + } + +}