diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Chunk.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Chunk.java index 39e77d4..c74bfa3 100644 --- a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Chunk.java +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Chunk.java @@ -2,382 +2,713 @@ import java.util.Arrays; import java.util.HashSet; +import java.util.Map; +import java.util.Random; import java.util.Set; +import java.util.stream.Stream; import codemetropolis.blockmodifier.ext.NBTException; import codemetropolis.blockmodifier.ext.NBTTag; public class Chunk { - + public NBTTag tag; - - private Chunk(NBTTag tag) { - if(tag.getType() != NBTTag.Type.TAG_Compound) { - try { - throw new NBTException("Chunk tag must be compound."); - } catch (NBTException e) { - e.printStackTrace(); - } - } - this.tag = tag; - - } - + + private Chunk(NBTTag tag) { + if (tag.getType() != NBTTag.Type.TAG_Compound) { + throw new IllegalArgumentException("Chunk tag must be compound."); + } + this.tag = tag; + } + public Chunk(int x, int z) { - + byte terrainPopulated = 1; long inhabitedTime = 0L; long lastUpdate = 0L; - + int[] heightMap = new int[256]; Arrays.fill(heightMap, 0); - - byte[] biomes = new byte[256]; - Arrays.fill(biomes, (byte)-1); - + NBTTag terrainPopulatedTag = new NBTTag(NBTTag.Type.TAG_Byte, "TerrainPopulated", terrainPopulated); NBTTag xPosTag = new NBTTag(NBTTag.Type.TAG_Int, "xPos", x); NBTTag zPosTag = new NBTTag(NBTTag.Type.TAG_Int, "zPos", z); + NBTTag statusTag = new NBTTag(NBTTag.Type.TAG_String, "Status", "empty"); NBTTag inhabitedTimeTag = new NBTTag(NBTTag.Type.TAG_Long, "InhabitedTime", inhabitedTime); NBTTag lastUpdateTag = new NBTTag(NBTTag.Type.TAG_Long, "LastUpdate", lastUpdate); - NBTTag biomesTag = new NBTTag(NBTTag.Type.TAG_Byte_Array, "Biomes", biomes); NBTTag entitiesTag = new NBTTag(NBTTag.Type.TAG_List, "Entities", NBTTag.Type.TAG_Byte); NBTTag tileEntitiesTag = new NBTTag(NBTTag.Type.TAG_List, "TileEntities", NBTTag.Type.TAG_Byte); NBTTag heightMapTag = new NBTTag(NBTTag.Type.TAG_Int_Array, "HeightMap", heightMap); NBTTag sectionsTag = new NBTTag("Sections", NBTTag.Type.TAG_List); - - NBTTag[] tagList = new NBTTag[] {terrainPopulatedTag, xPosTag, zPosTag, inhabitedTimeTag, biomesTag, lastUpdateTag, sectionsTag, entitiesTag, tileEntitiesTag, heightMapTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + NBTTag tileTicksTag = new NBTTag("TileTicks", NBTTag.Type.TAG_List); + + NBTTag[] tagList = new NBTTag[] { xPosTag, terrainPopulatedTag, zPosTag, inhabitedTimeTag, statusTag, + lastUpdateTag, sectionsTag, entitiesTag, tileEntitiesTag, heightMapTag, tileTicksTag, NBTTag.END_TAG }; NBTTag levelTag = new NBTTag(NBTTag.Type.TAG_Compound, "Level", tagList); - this.tag = new NBTTag(NBTTag.Type.TAG_Compound, "", new NBTTag[]{ levelTag, new NBTTag(NBTTag.Type.TAG_End, null, null) }); - + // NBTTag dataVersion = new NBTTag(NBTTag.Type.TAG_Int, "DataVersion", 1631); + this.tag = new NBTTag(NBTTag.Type.TAG_Compound, "", + new NBTTag[] { levelTag/* , dataVersion */, NBTTag.END_TAG }); + } - + public static Chunk parseNBT(NBTTag t) { return new Chunk(t); } - + public NBTTag toNBT() { return tag; } - - public void setBlock(int x, int y, int z, byte type, byte data) { + + public void setBlock(int x, int y, int z, String blockId, Map blockProperties) { + setBlock(x, y, z, blockId, PropertiesToTags(blockProperties)); + } + public void setBlock(int x, int y, int z, String blockId, NBTTag[] blockProperties) { int index = y >> 4; NBTTag section = getSection(index); - if(section == null) { + if (section == null) { section = addSection(index); } - + int blockIndex = (y % 16) * 256 + z * 16 + x; - ((byte[]) section.getSubtagByName("Blocks").getValue()) [blockIndex] = type; - - boolean lastBits = ((double)x / 2) % 1 == 0 ? true : false; - byte value = ((byte[]) section.getSubtagByName("Data").getValue()) [blockIndex / 2]; - if(lastBits) { - value = (byte)((value & 0xF0) | data); + + long[] blockStates = (long[]) section.getSubtagByName("BlockStates").getValue(); + NBTTag palette = section.getSubtagByName("Palette"); + + long paletteIndex = getIndexFromPalette(palette, blockId, blockProperties); + int numberOfBitsUsedToRepresentPalette; + + int cX = (int) tag.getSubtagByName("Level").getSubtagByName("xPos").getValue(); + int cZ = (int) tag.getSubtagByName("Level").getSubtagByName("zPos").getValue(); + + if (paletteIndex == -1) { + int oldNumberOfBitsUsedToRepresentPalette = getNumberOfBitsUsedToRepresentPalette(palette); + + paletteIndex = insertIntoPalette(palette, blockId, blockProperties); + + numberOfBitsUsedToRepresentPalette = getNumberOfBitsUsedToRepresentPalette(palette); + if (oldNumberOfBitsUsedToRepresentPalette != numberOfBitsUsedToRepresentPalette) { + + //int cY = y/16; + //System.out.println("ChangeBlockStateBitRepresentation("+x+" "+y%16+" "+z+" in "+cX+" "+cY+" "+cZ+" ["+(cX*16+x)+" "+y+" "+(cZ*16+z)+"], bId:"+blockId+", from:"+oldNumberOfBitsUsedToRepresentPalette+", to:"+numberOfBitsUsedToRepresentPalette+")"); + + blockStates = changeBlockStateBitRepresentation(blockStates, 4096, + oldNumberOfBitsUsedToRepresentPalette, numberOfBitsUsedToRepresentPalette); + section.getSubtagByName("BlockStates").setValue(blockStates); + } } else { - value = (byte)((value & 0x0F) | (data << 4)); + numberOfBitsUsedToRepresentPalette = getNumberOfBitsUsedToRepresentPalette(palette); } - ((byte[]) section.getSubtagByName("Data").getValue()) [blockIndex / 2] = value; - - int[] heightMap = (int[])tag.getSubtagByName("Level").getSubtagByName("HeightMap").getValue(); - - if(heightMap[z * 16 + x] < y + 1) + + setBlockState(blockStates, blockIndex, paletteIndex, numberOfBitsUsedToRepresentPalette); + + int[] heightMap = (int[]) tag.getSubtagByName("Level").getSubtagByName("HeightMap").getValue(); + + if (heightMap[z * 16 + x] < y + 1) heightMap[z * 16 + x] = y + 1; + + if (blockId.equals("minecraft:repeating_command_block")) { + NBTTag blockIdTag = new NBTTag(NBTTag.Type.TAG_String, "i", blockId); + NBTTag delayTag = new NBTTag(NBTTag.Type.TAG_Int, "t", new Random().nextInt(10)); + NBTTag priorityTag = new NBTTag(NBTTag.Type.TAG_Int, "p", 0); + NBTTag posXTag = new NBTTag(NBTTag.Type.TAG_Int, "x", cX * 16 + x); + NBTTag posYTag = new NBTTag(NBTTag.Type.TAG_Int, "y", y); + NBTTag posZTag = new NBTTag(NBTTag.Type.TAG_Int, "z", cZ * 16 + z); + NBTTag tileTickTag = new NBTTag(NBTTag.Type.TAG_Compound, "", + new NBTTag[] { blockIdTag, delayTag, priorityTag, posXTag, posYTag, posZTag, NBTTag.END_TAG }); + + tag.getSubtagByName("Level").getSubtagByName("TileTicks").addTag(tileTickTag); + } + } + + public String getBlockId(int x, int y, int z) { + int index = y >> 4; + NBTTag section = getSection(index); + if (section == null) { + section = addSection(index); + } + + long[] blockStates = (long[]) section.getSubtagByName("BlockStates").getValue(); + NBTTag palette = section.getSubtagByName("Palette"); + int numberOfBitsUsedToRepresentPalette = getNumberOfBitsUsedToRepresentPalette(palette); + int blockIndex = (y % 16) * 256 + z * 16 + x; + + return (String) palette + .getSubtags()[(int) getBlockState(blockStates, blockIndex, numberOfBitsUsedToRepresentPalette)] + .getSubtagByName("Name").getValue(); + } + + public static long getIndexFromPalette(NBTTag palette, String blockId, NBTTag[] blockProperties) { + NBTTag[] subtags = palette.getSubtags(); + + tagsLoop: for (int i = 0; i < subtags.length; i++) { + NBTTag t = subtags[i]; + + if (t.getSubtagByName("Name").getValue().equals(blockId)) { + NBTTag propertiesTag = t.getSubtagByName("Properties"); + + if( propertiesTag == null || propertiesTag.getValue() == null || ((NBTTag[]) propertiesTag.getValue()).length == 0 ) { + if( blockProperties == null || blockProperties.length == 0 ) { + return i; + }else { + return -1; + } + } + + NBTTag[] properties = (NBTTag[]) propertiesTag.getValue(); + + if (blockProperties.length != properties.length) { + continue; + } + + expectedLoop: for (NBTTag expected : blockProperties) { + for (NBTTag given : properties) { + String expectedName = expected.getName(); + String givenName = given.getName(); + if (expectedName == null || givenName == null) { + if (expectedName != givenName) { + continue; + } else { + continue expectedLoop; + } + + } + if (!expectedName.equals(givenName)) { + continue; + } + + String expectedValue = (String) expected.getValue(); + String givenValue = (String) given.getValue(); + + if ((expectedValue == null || givenValue == null) && expectedValue != givenValue) { + continue; + } + if (!expectedValue.equals(givenValue)) { + continue; + } + + continue expectedLoop; + } + continue tagsLoop; + } + return i; + } + } + return -1; + } + + public static long insertIntoPalette(NBTTag palette, String blockId, NBTTag[] blockProperties) { + if (0 < blockProperties.length + && blockProperties[blockProperties.length - 1].getType() != NBTTag.Type.TAG_End) { + throw new IllegalArgumentException("BlockProperties must be empty or have a TAG_End type item at the end."); + } + + NBTTag blockNameTag = new NBTTag(NBTTag.Type.TAG_String, "Name", blockId); + NBTTag[] blockStateTagList; + if (blockProperties.length > 1) { + NBTTag blockPropertiesTag = new NBTTag(NBTTag.Type.TAG_Compound, "Properties", blockProperties); + blockStateTagList = new NBTTag[] { blockNameTag, blockPropertiesTag, NBTTag.END_TAG }; + } else { + blockStateTagList = new NBTTag[] { blockNameTag, NBTTag.END_TAG }; + } + NBTTag blockStateTag = new NBTTag(NBTTag.Type.TAG_Compound, "", blockStateTagList); + + palette.addTag(blockStateTag); + return palette.getSubtags().length - 1; + } + + public static int getNumberOfBitsUsedToRepresentPalette(NBTTag palette) { + int elements = palette.getSubtags().length; + double log2 = Math.log(elements) / Math.log(2); + int bitsNeeded = (int) Math.ceil(log2); + return Math.max(4, bitsNeeded); + } + + public static void setBlockState(long[] blockStates, int index, long value, int numberOfUsedBits) { + if (numberOfUsedBits < 4) { + throw new IllegalArgumentException("Number of used bits must not be less than 4"); + } + + int startBit = index * numberOfUsedBits; + int startELement = startBit / 64; + int startBitOfElement = startBit - (startELement * 64); + + int elementIndex = startELement; + int bitIndex = startBitOfElement; + int bitsLeft = numberOfUsedBits; + + while (true) { + long mask = createMask(bitIndex, bitsLeft); + + long toBeInserted = value << bitIndex; + blockStates[elementIndex] = (blockStates[elementIndex] & mask) | toBeInserted; + + int insertedBits = 64 - bitIndex; + if (insertedBits < bitsLeft) { + elementIndex++; + bitIndex = 0; + bitsLeft -= insertedBits; + value >>>= insertedBits; + } else { + break; + } + } + } + + public static long createMask(int start, int length) { + long mask = 0; + + if (start > 0) { + mask |= 0xFFFFFFFFFFFFFFFFL >>> (64 - start); + } + + if (start + length < 64) { + mask |= 0xFFFFFFFFFFFFFFFFL << (start + length); + } + + return mask; } + public static long createInverseMask(int start, int length) { + return ~createMask(start, length); + } + + public static long getBlockState(long[] blockStates, int index, int numberOfUsedBits) { + if (numberOfUsedBits < 4) { + throw new IllegalArgumentException("Number of used bits must not be less than 4"); + } + + long value = 0; + + int startBit = index * numberOfUsedBits; + int startELement = startBit / 64; + int startBitOfElement = startBit - (startELement * 64); + + int elementIndex = startELement; + int bitIndex = startBitOfElement; + int bitsLeft = numberOfUsedBits; + + while (true) { + long mask = createInverseMask(bitIndex, bitsLeft); + long masked = blockStates[elementIndex] & mask; + long maskedAligned = masked >>> bitIndex; + value |= maskedAligned << (numberOfUsedBits - bitsLeft); + + int readBits = 64 - bitIndex; + if (readBits < bitsLeft) { + elementIndex++; + bitIndex = 0; + bitsLeft -= readBits; + } else { + break; + } + } + + return value; + } + + public static long[] changeBlockStateBitRepresentation(long[] oldBlockStates, int numberOfBlocks, int oldUsedBits, + int newUsedBits) { + int newBlockStatesSize = (int)Math.ceil((numberOfBlocks * newUsedBits) / 64f); + long[] newBlockStates = new long[newBlockStatesSize]; + for (int i = 0; i < numberOfBlocks; i++) { + long value = getBlockState(oldBlockStates, i, oldUsedBits); + setBlockState(newBlockStates, i, value, newUsedBits); + } + return newBlockStates; + } + + private String toTextJson(String text) { + return String.format("{\"text\":\"%s\"}", text); + } + public void setSignText(int x, int y, int z, String text) { - + String[] texts = new String[4]; - for(int i = 0; i < 4; i++) { - if(text.length() > 15) { + for (int i = 0; i < 4; i++) { + if (text.length() > 15) { texts[i] = text.substring(0, 14); text = text.substring(14); - } else if(text.length() > 0) { + } else if (text.length() > 0) { texts[i] = text.substring(0); text = ""; } else { texts[i] = ""; } + + texts[i] = toTextJson(texts[i]); } - + NBTTag tileEntities = tag.getSubtagByName("Level").getSubtagByName("TileEntities"); - for(NBTTag t : (NBTTag[])tileEntities.getValue()) { - if( - (int)t.getSubtagByName("x").getValue() == x && - (int)t.getSubtagByName("y").getValue() == y && - (int)t.getSubtagByName("z").getValue() == z && - ((String)t.getSubtagByName("id").getValue()).equals("Sign") - ) { - - for(int i = 0; i < texts.length; i++) { - t.getSubtagByName("Text" + (i + 1)).setValue(texts[i]); + for (NBTTag t : (NBTTag[]) tileEntities.getValue()) { + if ((int) t.getSubtagByName("x").getValue() == x && (int) t.getSubtagByName("y").getValue() == y + && (int) t.getSubtagByName("z").getValue() == z + && ((String) t.getSubtagByName("id").getValue()).equals("minecraft:sign")) { + + for (int i = 0; i < texts.length; i++) { + t.getSubtagByName("Text" + (i + 1)).setValue(texts[i]); } return; } } - + NBTTag xTag = new NBTTag(NBTTag.Type.TAG_Int, "x", x); NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Int, "y", y); NBTTag zTag = new NBTTag(NBTTag.Type.TAG_Int, "z", z); - NBTTag idTag = new NBTTag(NBTTag.Type.TAG_String, "id", "Sign"); + NBTTag idTag = new NBTTag(NBTTag.Type.TAG_String, "id", "minecraft:sign"); NBTTag text1Tag = new NBTTag(NBTTag.Type.TAG_String, "Text1", texts[0]); NBTTag text2Tag = new NBTTag(NBTTag.Type.TAG_String, "Text2", texts[1]); NBTTag text3Tag = new NBTTag(NBTTag.Type.TAG_String, "Text3", texts[2]); NBTTag text4Tag = new NBTTag(NBTTag.Type.TAG_String, "Text4", texts[3]); - NBTTag[] tagList = new NBTTag[] {xTag, yTag, zTag, idTag, text1Tag, text2Tag, text3Tag, text4Tag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + NBTTag[] tagList = new NBTTag[] { xTag, yTag, zTag, idTag, text1Tag, text2Tag, text3Tag, text4Tag, + NBTTag.END_TAG }; NBTTag tileEntityTag = new NBTTag(NBTTag.Type.TAG_Compound, "", tagList); - + tileEntities.addTag(tileEntityTag); - + } - + + public void setCommandBlockCommand(int x, int y, int z, String command) { + + NBTTag tileEntities = tag.getSubtagByName("Level").getSubtagByName("TileEntities"); + for (NBTTag t : (NBTTag[]) tileEntities.getValue()) { + if ((int) t.getSubtagByName("x").getValue() == x && (int) t.getSubtagByName("y").getValue() == y + && (int) t.getSubtagByName("z").getValue() == z + && ((String) t.getSubtagByName("id").getValue()).equals("minecraft:command_block")) { + + t.getSubtagByName("Command").setValue(command); + return; + } + } + + NBTTag xTag = new NBTTag(NBTTag.Type.TAG_Int, "x", x); + NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Int, "y", y); + NBTTag zTag = new NBTTag(NBTTag.Type.TAG_Int, "z", z); + NBTTag idTag = new NBTTag(NBTTag.Type.TAG_String, "id", "minecraft:command_block"); + NBTTag commandTag = new NBTTag(NBTTag.Type.TAG_String, "Command", command); + NBTTag lastOutputTag = new NBTTag(NBTTag.Type.TAG_String, "LastOutput", ""); + NBTTag autoTag = new NBTTag(NBTTag.Type.TAG_Byte, "auto", (byte) 1); + NBTTag conditionMetTag = new NBTTag(NBTTag.Type.TAG_Byte, "conditionMet", (byte) 1); + NBTTag keepPackedTag = new NBTTag(NBTTag.Type.TAG_Byte, "keepPacked", (byte) 0); + NBTTag poweredTag = new NBTTag(NBTTag.Type.TAG_Byte, "powered", (byte) 1); + NBTTag successCountTag = new NBTTag(NBTTag.Type.TAG_Int, "SuccessCount", 0); + NBTTag trackOutputTag = new NBTTag(NBTTag.Type.TAG_Byte, "TrackOutput", (byte) 1); + NBTTag updateLastExecutionTag = new NBTTag(NBTTag.Type.TAG_Byte, "UpdateLastExecution", (byte) 1); + NBTTag lastExecutionTag = new NBTTag(NBTTag.Type.TAG_Long, "LastExecution", (long) 0); + NBTTag customNameTag = new NBTTag(NBTTag.Type.TAG_String, "CustomName", "{\"text\":\"@\"}"); + + NBTTag[] tagList = new NBTTag[] { xTag, yTag, zTag, idTag, commandTag, lastOutputTag, autoTag, customNameTag, + conditionMetTag, lastExecutionTag, keepPackedTag, poweredTag, successCountTag, trackOutputTag, + updateLastExecutionTag, NBTTag.END_TAG }; + NBTTag tileEntityTag = new NBTTag(NBTTag.Type.TAG_Compound, "", tagList); + + tileEntities.addTag(tileEntityTag); + + } + public void setBannerColor(int x, int y, int z, int color) { - + NBTTag tileEntities = tag.getSubtagByName("Level").getSubtagByName("TileEntities"); - for(NBTTag t : (NBTTag[])tileEntities.getValue()) { - if( - (int)t.getSubtagByName("x").getValue() == x && - (int)t.getSubtagByName("y").getValue() == y && - (int)t.getSubtagByName("z").getValue() == z && - ((String)t.getSubtagByName("id").getValue()).equals("Banner") - ) { + for (NBTTag t : (NBTTag[]) tileEntities.getValue()) { + if ((int) t.getSubtagByName("x").getValue() == x && (int) t.getSubtagByName("y").getValue() == y + && (int) t.getSubtagByName("z").getValue() == z + && ((String) t.getSubtagByName("id").getValue()).equals("Banner")) { t.getSubtagByName("Base").setValue(color); return; } } - + NBTTag baseTag = new NBTTag(NBTTag.Type.TAG_Int, "Base", color); NBTTag xTag = new NBTTag(NBTTag.Type.TAG_Int, "x", x); NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Int, "y", y); NBTTag zTag = new NBTTag(NBTTag.Type.TAG_Int, "z", z); NBTTag idTag = new NBTTag(NBTTag.Type.TAG_String, "id", "Banner"); - NBTTag[] tagList = new NBTTag[] {baseTag, xTag, yTag, zTag, idTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + NBTTag[] tagList = new NBTTag[] { baseTag, xTag, yTag, zTag, idTag, NBTTag.END_TAG }; NBTTag tileEntityTag = new NBTTag(NBTTag.Type.TAG_Compound, "", tagList); - + tileEntities.addTag(tileEntityTag); - + } - + public void removeSignText(int x, int y, int z) { removeTileEntity(x, y, z, "Sign"); } - + public void addChestItem(int x, int y, int z, int id, int quantity) { NBTTag tileEntity = getTileEntity(x, y, z, "Chest"); - - if(tileEntity == null) { + + if (tileEntity == null) { NBTTag xTag = new NBTTag(NBTTag.Type.TAG_Int, "x", x); NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Int, "y", y); NBTTag zTag = new NBTTag(NBTTag.Type.TAG_Int, "z", z); NBTTag idTag = new NBTTag(NBTTag.Type.TAG_String, "id", "Chest"); NBTTag itemsTag = new NBTTag("Items", NBTTag.Type.TAG_Compound); - NBTTag[] tagList = new NBTTag[] {xTag, yTag, zTag, idTag, itemsTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + NBTTag[] tagList = new NBTTag[] { xTag, yTag, zTag, idTag, itemsTag, NBTTag.END_TAG }; tileEntity = new NBTTag(NBTTag.Type.TAG_Compound, "", tagList); tag.getSubtagByName("Level").getSubtagByName("TileEntities").addTag(tileEntity); } - + NBTTag items = tileEntity.getSubtagByName("Items"); Set usedSlots = new HashSet(); - for(NBTTag t : (NBTTag[])items.getValue()) { - usedSlots.add((byte)t.getSubtagByName("Slot").getValue()); + for (NBTTag t : (NBTTag[]) items.getValue()) { + usedSlots.add((byte) t.getSubtagByName("Slot").getValue()); } - - for(byte i = 0; i < 27; i++) { - if(!usedSlots.contains(i)) { - NBTTag idTag = new NBTTag(NBTTag.Type.TAG_Short, "id", (short)id); + + for (byte i = 0; i < 27; i++) { + if (!usedSlots.contains(i)) { + NBTTag idTag = new NBTTag(NBTTag.Type.TAG_Short, "id", (short) id); NBTTag slotTag = new NBTTag(NBTTag.Type.TAG_Byte, "Slot", i); - NBTTag countTag = new NBTTag(NBTTag.Type.TAG_Byte, "Count", (byte)quantity); - NBTTag damageTag = new NBTTag(NBTTag.Type.TAG_Short, "Damage", (short)0); - NBTTag[] tagList = new NBTTag[] {idTag, slotTag, countTag, damageTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + NBTTag countTag = new NBTTag(NBTTag.Type.TAG_Byte, "Count", (byte) quantity); + NBTTag damageTag = new NBTTag(NBTTag.Type.TAG_Short, "Damage", (short) 0); + NBTTag[] tagList = new NBTTag[] { idTag, slotTag, countTag, damageTag, NBTTag.END_TAG }; items.addTag(new NBTTag(NBTTag.Type.TAG_Compound, "", tagList)); return; } } - + } - + public void clearChestItems(int x, int y, int z) { removeTileEntity(x, y, z, "Chest"); } private NBTTag getTileEntity(int x, int y, int z, String id) { NBTTag tileEntities = tag.getSubtagByName("Level").getSubtagByName("TileEntities"); - for(NBTTag t : (NBTTag[])tileEntities.getValue()) { - if( - (int)t.getSubtagByName("x").getValue() == x && - (int)t.getSubtagByName("y").getValue() == y && - (int)t.getSubtagByName("z").getValue() == z && - ((String)t.getSubtagByName("id").getValue()).equals(id) - ) { + for (NBTTag t : (NBTTag[]) tileEntities.getValue()) { + if ((int) t.getSubtagByName("x").getValue() == x && (int) t.getSubtagByName("y").getValue() == y + && (int) t.getSubtagByName("z").getValue() == z + && ((String) t.getSubtagByName("id").getValue()).equals(id)) { return t; } } return null; } - + public void removeTileEntity(int x, int y, int z, String id) { - + Set tagsToRemoveIndex = new HashSet(); NBTTag tileEntities = tag.getSubtagByName("Level").getSubtagByName("TileEntities"); - NBTTag[] tileEntitiesArray = (NBTTag[])tileEntities.getValue(); - for(int i = 0; i < tileEntitiesArray.length; i++) { + NBTTag[] tileEntitiesArray = (NBTTag[]) tileEntities.getValue(); + for (int i = 0; i < tileEntitiesArray.length; i++) { NBTTag t = tileEntitiesArray[i]; - if( - (int)t.getSubtagByName("x").getValue() == x && - (int)t.getSubtagByName("y").getValue() == y && - (int)t.getSubtagByName("z").getValue() == z - ) { - if(id == null) { + if ((int) t.getSubtagByName("x").getValue() == x && (int) t.getSubtagByName("y").getValue() == y + && (int) t.getSubtagByName("z").getValue() == z) { + if (id == null) { tagsToRemoveIndex.add(i); - } else if(((String)t.getSubtagByName("id").getValue()).equals(id)) { + } else if (((String) t.getSubtagByName("id").getValue()).equals(id)) { tagsToRemoveIndex.add(i); break; } } } - for(int i : tagsToRemoveIndex) - tileEntities.removeTag(i); - + for (int i : tagsToRemoveIndex) + tileEntities.removeTag(i); + } - + public void clearTileEntitiesAt(int x, int y, int z) { removeTileEntity(x, y, z, null); } - + private NBTTag getSection(int y) { - for(NBTTag t : tag.getSubtagByName("Level").getSubtagByName("Sections").getSubtags()) { - if((byte)t.getSubtagByName("Y").getValue() == y) { + for (NBTTag t : tag.getSubtagByName("Level").getSubtagByName("Sections").getSubtags()) { + if ((byte) t.getSubtagByName("Y").getValue() == y) { return t; } } return null; } - + private NBTTag addSection(int y) { - + byte[] blockLight = new byte[2048]; - Arrays.fill(blockLight, (byte)0); - - byte[] blocks = new byte[4096]; - Arrays.fill(blocks, (byte)0); - - byte[] data = new byte[2048]; - Arrays.fill(data, (byte)0); - + Arrays.fill(blockLight, (byte) 0); + + long[] blockStates = new long[256]; + Arrays.fill(blockStates, (long) 0); + byte[] skyLight = new byte[2048]; - Arrays.fill(skyLight, (byte)255); - - - NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Byte, "Y", (byte)y); + Arrays.fill(skyLight, (byte) 255); + + NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Byte, "Y", (byte) y); + NBTTag paletteTag = new NBTTag("Palette", NBTTag.Type.TAG_List); + insertIntoPalette(paletteTag, "minecraft:air", new NBTTag[0]); NBTTag blockLightTag = new NBTTag(NBTTag.Type.TAG_Byte_Array, "BlockLight", blockLight); - NBTTag blocksTag = new NBTTag(NBTTag.Type.TAG_Byte_Array, "Blocks", blocks); - NBTTag dataTag = new NBTTag(NBTTag.Type.TAG_Byte_Array, "Data", data); + NBTTag blockStatesTag = new NBTTag(NBTTag.Type.TAG_Long_Array, "BlockStates", blockStates); NBTTag skyLightTag = new NBTTag(NBTTag.Type.TAG_Byte_Array, "SkyLight", skyLight); - NBTTag[] tagList = new NBTTag[] {dataTag, skyLightTag, blockLightTag, yTag, blocksTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + NBTTag[] tagList = new NBTTag[] { yTag, paletteTag, skyLightTag, blockStatesTag, blockLightTag, + NBTTag.END_TAG }; NBTTag sectionTag = new NBTTag(NBTTag.Type.TAG_Compound, "", tagList); tag.getSubtagByName("Level").getSubtagByName("Sections").addTag(sectionTag); - + return sectionTag; } - - public NBTTag addSection(int y, byte[] blocks, byte[] skyLight) { + + public NBTTag addSection(int y, NBTTag palette, long[] blockStates, byte[] skyLight) { NBTTag section = addSection(y); - section.getSubtagByName("Blocks").setValue(blocks); + + section.removeSubtag(section.getSubtagByName("Palette")); // need to replace, cuz setvalue didn't wanna work + section.addTag(palette); + + section.getSubtagByName("BlockStates").setValue(blockStates); section.getSubtagByName("SkyLight").setValue(skyLight); return section; } - - public NBTTag addSectionFilled(int y, byte type, int height) { - byte[] blocks = new byte[4096]; + + public NBTTag addSectionFilled(int y, String blockId, NBTTag[] blockProperties, int height) { + + NBTTag palette = new NBTTag("Palette", NBTTag.Type.TAG_List); + long airIndex = 0; + long blockIndex = 0; + + if (height < 16) { + insertIntoPalette(palette, "minecraft:air", new NBTTag[0]); + blockIndex++; + } + insertIntoPalette(palette, blockId, blockProperties); + + long[] blockStates = new long[256]; byte[] skyLight = new byte[2048]; - - for(int _x = 0; _x < 16; _x++) { - for(int _y = 0; _y < 16; _y++) { - for(int _z = 0; _z < 16; _z++) { - if(_y < height) { - blocks[_y * 256 + _z * 16 + _x] = type; + + for (int _x = 0; _x < 16; _x++) { + for (int _y = 0; _y < 16; _y++) { + for (int _z = 0; _z < 16; _z++) { + if (_y < height) { + setBlockState(blockStates, _y * 256 + _z * 16 + _x, blockIndex, 4); } else { - blocks[_y * 256 + _z * 16 + _x] = 0; + setBlockState(blockStates, _y * 256 + _z * 16 + _x, airIndex, 4); } } } } - - int[] heightMap = (int[])tag.getSubtagByName("Level").getSubtagByName("HeightMap").getValue(); - for(int i = 0; i < 256; i++) { - if(heightMap[i] < y * 16 + height) + + int[] heightMap = (int[]) tag.getSubtagByName("Level").getSubtagByName("HeightMap").getValue(); + for (int i = 0; i < 256; i++) { + if (heightMap[i] < y * 16 + height) heightMap[i] = y * 16 + height; } - - Arrays.fill(skyLight, 0, height * 128, (byte)0); - Arrays.fill(skyLight, height * 128, 2048, (byte)255); - - return addSection(y, blocks, skyLight); + + Arrays.fill(skyLight, 0, height * 128, (byte) 0); + Arrays.fill(skyLight, height * 128, 2048, (byte) 255); + + return addSection(y, palette, blockStates, skyLight); } - - public NBTTag addSectionFilled(int y, byte type) { - return addSectionFilled(y, type, 16); + + public NBTTag addSectionFilled(int y, String blockId, NBTTag[] blockProperties) { + return addSectionFilled(y, blockId, blockProperties, 16); } - - public void fill(int y, byte type) { - - for(int _y = 0; _y < (y >> 4); _y++) { - addSectionFilled(_y, type); + + public void fill(int y, String blockId, NBTTag[] blockProperties) { + + for (int _y = 0; _y < (y >> 4); _y++) { + addSectionFilled(_y, blockId, blockProperties); } - addSectionFilled(y >> 4, type, y % 16 + 1); - + addSectionFilled(y >> 4, blockId, blockProperties, y % 16 + 1); + } - + public void calculateLighting() { - byte[] lightingObjects = new byte[] {50, 124}; + Set lightingObjects = new HashSet<>(); + lightingObjects.add("minecraft:redstone_lamp"); + lightingObjects.add("minecraft:wall_torch"); + + NBTTag[] sectionTags = tag.getSubtagByName("Level").getSubtagByName("Sections").getSubtags(); + for (NBTTag section : sectionTags) { + byte[] skyLight = (byte[]) section.getSubtagByName("SkyLight").getValue(); + byte[] blockLight = (byte[]) section.getSubtagByName("BlockLight").getValue(); + long[] blockStates = (long[]) section.getSubtagByName("BlockStates").getValue(); + NBTTag palette = section.getSubtagByName("Palette"); + int paletteBits = getNumberOfBitsUsedToRepresentPalette(palette); + for (int blockZ = 0; blockZ < 16; blockZ++) + for (int y = 0; y < 16; y++) + for (int blockX = 0; blockX < 16; blockX++) { + // Temporary solution + int paletteId = (int) getBlockState(blockStates, y * 256 + blockZ * 16 + blockX, paletteBits); + String blockId = (String) palette.getSubtags()[paletteId].getSubtagByName("Name").getValue(); + + if (!blockId.equals("minecraft:air")) { + setNibble(skyLight, (y * 256 + blockZ * 16 + blockX), (byte) 15); + if (lightingObjects.contains(blockId)) { + setNibble(blockLight, (y * 256 + blockZ * 16 + blockX), (byte) 15); + setNibble(blockLight, (y * 256 + blockZ * 16 + blockX + 1), (byte) 15); + setNibble(blockLight, (y * 256 + blockZ * 16 + blockX - 1), (byte) 15); + setNibble(blockLight, ((y + 1) * 256 + blockZ * 16 + blockX), (byte) 15); + setNibble(blockLight, ((y - 1) * 256 + blockZ * 16 + blockX), (byte) 15); + setNibble(blockLight, (y * 256 + (blockZ + 1) * 16 + blockX), (byte) 15); + setNibble(blockLight, (y * 256 + (blockZ - 1) * 16 + blockX), (byte) 15); + } + } + + } + section.getSubtagByName("SkyLight").setValue(skyLight); + section.getSubtagByName("BlockLight").setValue(blockLight); + } + } + + public void filterMagicalBlocks() { NBTTag[] sectionTags = tag.getSubtagByName("Level").getSubtagByName("Sections").getSubtags(); - for(NBTTag section : sectionTags) { - byte[] skyLight = (byte[])section.getSubtagByName("SkyLight").getValue(); - byte[] blockLight = (byte[])section.getSubtagByName("BlockLight").getValue(); - byte[] blocks = (byte[])section.getSubtagByName("Blocks").getValue(); - //int sectionY = (byte)section.getSubtagByName("Y").getValue(); - for(int blockZ = 0; blockZ < 16; blockZ++) - for(int y = 0; y < 16; y++) - for(int blockX = 0; blockX < 16; blockX++) { - //Temporary solution - if(blocks[y * 256 + blockZ * 16 + blockX] != 0) { - setNibble(skyLight, (y * 256 + blockZ * 16 + blockX), (byte) 0); - for(byte b : lightingObjects) { - if(blocks[y * 256 + blockZ * 16 + blockX] == b) { - setNibble(blockLight, (y * 256 + blockZ * 16 + blockX), (byte) 15); - setNibble(blockLight, (y * 256 + blockZ * 16 + blockX + 1), (byte) 15); - setNibble(blockLight, (y * 256 + blockZ * 16 + blockX - 1), (byte) 15); - setNibble(blockLight, ((y + 1) * 256 + blockZ * 16 + blockX), (byte) 15); - setNibble(blockLight, ((y - 1) * 256 + blockZ * 16 + blockX), (byte) 15); - setNibble(blockLight, (y * 256 + (blockZ + 1) * 16 + blockX), (byte) 15); - setNibble(blockLight, (y * 256 + (blockZ - 1) * 16 + blockX), (byte) 15); - } - } - } - - } - section.getSubtagByName("SkyLight").setValue(skyLight); - section.getSubtagByName("BlockLight").setValue(blockLight); - } + int chunkX = (int) tag.getSubtagByName("Level").getSubtagByName("xPos").getValue(); + int chunkZ = (int) tag.getSubtagByName("Level").getSubtagByName("zPos").getValue(); + + for (NBTTag section : sectionTags) { + int sectionY = (byte) section.getSubtagByName("Y").getValue(); + + for (int blockZ = 0; blockZ < 16; blockZ++) { + for (int y = 0; y < 16; y++) { + for (int blockX = 0; blockX < 16; blockX++) { + + long[] blockStates = (long[]) section.getSubtagByName("BlockStates").getValue(); + NBTTag palette = section.getSubtagByName("Palette"); + int paletteBits = getNumberOfBitsUsedToRepresentPalette(palette); + + int paletteId = (int) getBlockState(blockStates, y * 256 + blockZ * 16 + blockX, paletteBits); + String blockId = (String) palette.getSubtags()[paletteId].getSubtagByName("Name").getValue(); + + if (blockId.equals("minecraft:oak_fence")) { + int absoluteY = sectionY * 16 + y; + int absoluteX = chunkX * 16 + blockX; + int absoluteZ = chunkZ * 16 + blockZ; + String command = "fill " + absoluteX + " " + absoluteY + " " + absoluteZ + " " + absoluteX + + " " + absoluteY + " " + absoluteZ + " " + blockId; + setBlock(blockX, absoluteY, blockZ, "minecraft:repeating_command_block", new NBTTag[] { + new NBTTag(NBTTag.Type.TAG_String, "conditional", "false"), NBTTag.END_TAG }); + setCommandBlockCommand(absoluteX, absoluteY, absoluteZ, command); + } + + } + } + } + } } - - private static void setNibble(byte[] a, int index, byte value) { - if(index / 2 < 0 || index / 2 > a.length - 1) return; - - boolean lastBits = ((double)index / 2) % 1 == 0 ? true : false; - - if(lastBits) { - value = (byte)((a[index / 2] & 0xF0) | value); + + public static void setNibble(byte[] a, int index, byte value) { + if (index / 2 < 0 || index / 2 > a.length - 1) + return; + + boolean lastBits = ((double) index / 2) % 1 == 0 ? true : false; + + if (lastBits) { + value = (byte) ((a[index / 2] & 0xF0) | value); } else { - value = (byte)((a[index / 2] & 0x0F) | (value << 4)); + value = (byte) ((a[index / 2] & 0x0F) | (value << 4)); } - - a[index / 2] = value; + + a[index / 2] = value; } @Override public String toString() { return tag.toString(); } - + + public static NBTTag[] PropertiesToTags(Map properties) { + if (properties.isEmpty()) { + return new NBTTag[0]; + } else { + return Stream.concat( + properties.entrySet().stream() + .map(e -> new NBTTag(NBTTag.Type.TAG_String, e.getKey(), e.getValue())), + Stream.of(NBTTag.END_TAG)).toArray(size -> new NBTTag[size]); + } + } + } diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Level.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Level.java index ab06de0..83ec87d 100644 --- a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Level.java +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Level.java @@ -5,9 +5,11 @@ import codemetropolis.blockmodifier.ext.LevelFile; import codemetropolis.blockmodifier.ext.NBTTag; +import codemetropolis.blockmodifier.helpers.LevelHelper; public class Level { + private static final LevelHelper HELPER = new LevelHelper(); private LevelFile levelFile; private NBTTag tag; @@ -15,60 +17,130 @@ public Level(World world) { File file = new File(String.format("%s/level.dat", world.PATH)); boolean alreadyExists = file.exists(); this.levelFile = new LevelFile(file); - - if(alreadyExists) { + + if (alreadyExists) { + try { tag = NBTTag.readFrom(levelFile.getLevelDataInputStream()); } catch (IOException e) { e.printStackTrace(); - } + } } else { - NBTTag allowCommandsTag = new NBTTag(NBTTag.Type.TAG_Byte, "allowCommands", (byte)0); - NBTTag hardcoreTag = new NBTTag(NBTTag.Type.TAG_Byte, "hardcore", (byte)0); - NBTTag initializedTag = new NBTTag(NBTTag.Type.TAG_Byte, "initialized", (byte)1); - NBTTag mapFeaturesTag = new NBTTag(NBTTag.Type.TAG_Byte, "MapFeatures", (byte)0); - NBTTag rainingTag = new NBTTag(NBTTag.Type.TAG_Byte, "raining", (byte)0); - NBTTag thunderingTag = new NBTTag(NBTTag.Type.TAG_Byte, "thundering", (byte)0); - NBTTag gameTypeTag = new NBTTag(NBTTag.Type.TAG_Int, "GameType", 1); - NBTTag generatorVersionTag = new NBTTag(NBTTag.Type.TAG_Int, "generatorVersion", 1); - NBTTag rainTimeTag = new NBTTag(NBTTag.Type.TAG_Int, "rainTime", Integer.MAX_VALUE); - NBTTag spawnXTag = new NBTTag(NBTTag.Type.TAG_Int, "SpawnX", 0); - NBTTag spawnYTag = new NBTTag(NBTTag.Type.TAG_Int, "SpawnY", world.GROUNDLEVEL + 1); - NBTTag spawnZTag = new NBTTag(NBTTag.Type.TAG_Int, "SpawnZ", 0); - NBTTag thunderTimeTag = new NBTTag(NBTTag.Type.TAG_Int, "thunderTime", Integer.MAX_VALUE); - NBTTag versionTag = new NBTTag(NBTTag.Type.TAG_Int, "version", 19133); - NBTTag dayTimeTag = new NBTTag(NBTTag.Type.TAG_Long, "DayTime", 3000L); - NBTTag lastPlayedTag = new NBTTag(NBTTag.Type.TAG_Long, "LastPlayed", 0L); - NBTTag randomSeedTag = new NBTTag(NBTTag.Type.TAG_Long, "RandomSeed", 0L); - NBTTag sizeOnDiskTag = new NBTTag(NBTTag.Type.TAG_Long, "SizeOnDisk", 0L); - NBTTag timeTag = new NBTTag(NBTTag.Type.TAG_Long, "Time", 3000L); - NBTTag generatorNameTag = new NBTTag(NBTTag.Type.TAG_String, "generatorName", "flat"); - NBTTag generatorOptionsTag = new NBTTag(NBTTag.Type.TAG_String, "generatorOptions", "3;minecraft:bedrock," + (world.GROUNDLEVEL - 1) + "*minecraft:dirt,minecraft:grass"); - NBTTag levelNameTag = new NBTTag(NBTTag.Type.TAG_String, "LevelName", world.NAME); - - NBTTag commandBlockOutputTag = new NBTTag(NBTTag.Type.TAG_String, "commandBlockOutput", "true"); - NBTTag doDaylightCycleTag = new NBTTag(NBTTag.Type.TAG_String, "doDaylightCycle", "false"); - NBTTag doFireTickTag = new NBTTag(NBTTag.Type.TAG_String, "doFireTick", "true"); - NBTTag doMobLootTag = new NBTTag(NBTTag.Type.TAG_String, "doMobLoot", "true"); - NBTTag doMobSpawningTag = new NBTTag(NBTTag.Type.TAG_String, "doMobSpawning", "false"); - NBTTag doTileDropsTag = new NBTTag(NBTTag.Type.TAG_String, "doTileDrops", "true"); - NBTTag keepInventoryTag = new NBTTag(NBTTag.Type.TAG_String, "keepInventory", "true"); - NBTTag mobGriefingTag = new NBTTag(NBTTag.Type.TAG_String, "mobGriefing", "false"); - NBTTag naturalRegenerationTag = new NBTTag(NBTTag.Type.TAG_String, "naturalRegeneration", "true"); - - NBTTag[] ruleList = new NBTTag[] {commandBlockOutputTag, doDaylightCycleTag, doFireTickTag, doMobLootTag, doMobSpawningTag, doTileDropsTag, - keepInventoryTag, mobGriefingTag, naturalRegenerationTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; - NBTTag gameRulesTag = new NBTTag(NBTTag.Type.TAG_Compound, "GameRules", ruleList); - - NBTTag[] tagList = new NBTTag[] {versionTag, initializedTag, levelNameTag, generatorNameTag, generatorVersionTag, generatorOptionsTag, randomSeedTag, - mapFeaturesTag, lastPlayedTag, sizeOnDiskTag, allowCommandsTag, hardcoreTag, gameTypeTag, timeTag, dayTimeTag, spawnXTag, spawnYTag, - spawnZTag, rainingTag, rainTimeTag, thunderingTag, thunderTimeTag, gameRulesTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; - NBTTag dataTag = new NBTTag(NBTTag.Type.TAG_Compound, "Data", tagList); - tag = new NBTTag(NBTTag.Type.TAG_Compound, "", new NBTTag[] {dataTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}); + + tag = createNewLevelFile(world); } - + } - + + private NBTTag createNewLevelFile(World world) { + NBTTag versionTag = new NBTTag(NBTTag.Type.TAG_Int, "version", 19133); + NBTTag initializedTag = new NBTTag(NBTTag.Type.TAG_Byte, "initialized", (byte) 1); + NBTTag levelNameTag = new NBTTag(NBTTag.Type.TAG_String, "LevelName", world.NAME); + NBTTag generatorNameTag = new NBTTag(NBTTag.Type.TAG_String, "generatorName", "flat"); + NBTTag generatorVersionTag = new NBTTag(NBTTag.Type.TAG_Int, "generatorVersion", 0); + NBTTag randomSeedTag = new NBTTag(NBTTag.Type.TAG_Long, "RandomSeed", 0L); + NBTTag mapFeaturesTag = new NBTTag(NBTTag.Type.TAG_Byte, "MapFeatures", (byte) 0); + NBTTag lastPlayedTag = new NBTTag(NBTTag.Type.TAG_Long, "LastPlayed", System.currentTimeMillis()); + NBTTag sizeOnDiskTag = new NBTTag(NBTTag.Type.TAG_Long, "SizeOnDisk", 0L); + NBTTag allowCommandsTag = new NBTTag(NBTTag.Type.TAG_Byte, "allowCommands", (byte) 1); + NBTTag hardcoreTag = new NBTTag(NBTTag.Type.TAG_Byte, "hardcore", (byte) 0); + NBTTag gameTypeTag = new NBTTag(NBTTag.Type.TAG_Int, "GameType", 1); + NBTTag difficultyTag = new NBTTag(NBTTag.Type.TAG_Byte, "Difficulty", (byte) 2); + NBTTag difficultyLockedTag = new NBTTag(NBTTag.Type.TAG_Byte, "DifficultyLocked", (byte) 0); + NBTTag timeTag = new NBTTag(NBTTag.Type.TAG_Long, "Time", 3000L); + NBTTag dayTimeTag = new NBTTag(NBTTag.Type.TAG_Long, "DayTime", 3000L); + NBTTag spawnXTag = new NBTTag(NBTTag.Type.TAG_Int, "SpawnX", 0); + NBTTag spawnYTag = new NBTTag(NBTTag.Type.TAG_Int, "SpawnY", world.GROUNDLEVEL + 1); + NBTTag spawnZTag = new NBTTag(NBTTag.Type.TAG_Int, "SpawnZ", 0); + NBTTag borderCenterXTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderCenterX", (double) 0); + NBTTag borderCenterZTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderCenterZ", (double) 0); + + NBTTag borderSizeTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderSize", (double) 60000000); + NBTTag borderSafeZoneTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderSafeZone", (double) 5); + NBTTag borderWarningBlocksTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderWarningBlocks", (double) 5); + NBTTag borderWarningTimeTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderWarningTime", (double) 15); + NBTTag borderSizeLerpTargetTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderSizeLerpTarget", (double) 60000000); + NBTTag borderSizeLerpTimeTag = new NBTTag(NBTTag.Type.TAG_Long, "BorderSizeLerpTime", 0L); + NBTTag borderDamagePerBlockTag = new NBTTag(NBTTag.Type.TAG_Double, "BorderSizeLerpTarget", 0.2); + + NBTTag dataVersionTag = new NBTTag(NBTTag.Type.TAG_Int, "DataVersion", 1631); + + NBTTag rainingTag = new NBTTag(NBTTag.Type.TAG_Byte, "raining", (byte) 0); + NBTTag rainTimeTag = new NBTTag(NBTTag.Type.TAG_Int, "rainTime", Integer.MAX_VALUE); + NBTTag thunderingTag = new NBTTag(NBTTag.Type.TAG_Byte, "thundering", (byte) 0); + NBTTag thunderTimeTag = new NBTTag(NBTTag.Type.TAG_Int, "thunderTime", Integer.MAX_VALUE); + NBTTag clearWeatherTimeTag = new NBTTag(NBTTag.Type.TAG_Int, "clearWeatherTime", Integer.MAX_VALUE); + + /** + * Gamerules: + * + */ + + NBTTag announceAdvancementsTag = new NBTTag(NBTTag.Type.TAG_String, "announceAdvancements", "true"); + NBTTag commandBlockOutputTag = new NBTTag(NBTTag.Type.TAG_String, "commandBlockOutput", "true"); + NBTTag disableElytraMovementCheckTag = new NBTTag(NBTTag.Type.TAG_String, "disableElytraMovementCheck", "false"); + NBTTag doDaylightCycleTag = new NBTTag(NBTTag.Type.TAG_String, "doDaylightCycle", "false"); + NBTTag doEntityDropsTag = new NBTTag(NBTTag.Type.TAG_String, "doEntityDrops", "true"); + NBTTag doFireTickTag = new NBTTag(NBTTag.Type.TAG_String, "doFireTick", "true"); + NBTTag doLimitedCraftingTag = new NBTTag(NBTTag.Type.TAG_String, "doLimitedCrafting", "false"); + NBTTag doMobLootTag = new NBTTag(NBTTag.Type.TAG_String, "doMobLoot", "true"); + NBTTag doMobSpawningTag = new NBTTag(NBTTag.Type.TAG_String, "doMobSpawning", "false"); + NBTTag doTileDropsTag = new NBTTag(NBTTag.Type.TAG_String, "doTileDrops", "true"); + NBTTag doWeatherCycleTag = new NBTTag(NBTTag.Type.TAG_String, "doWeatherCycle", "true"); + NBTTag keepInventoryTag = new NBTTag(NBTTag.Type.TAG_String, "keepInventory", "true"); + NBTTag logAdminCommandsTag = new NBTTag(NBTTag.Type.TAG_String, "logAdminCommands", "true"); + NBTTag maxCommandChainLengthTag = new NBTTag(NBTTag.Type.TAG_String, "maxCommandChainLength", "65536"); + NBTTag maxEntityCrammingTag = new NBTTag(NBTTag.Type.TAG_String, "maxEntityCramming", "24"); + NBTTag mobGriefingTag = new NBTTag(NBTTag.Type.TAG_String, "mobGriefing", "false"); + NBTTag naturalRegenerationTag = new NBTTag(NBTTag.Type.TAG_String, "naturalRegeneration", "true"); + NBTTag randomTickSpeedTag = new NBTTag(NBTTag.Type.TAG_String, "randomTickSpeed", "3"); + NBTTag reducedDebugInfoTag = new NBTTag(NBTTag.Type.TAG_String, "reducedDebugInfo", "false"); + NBTTag sendCommandFeedbackTag = new NBTTag(NBTTag.Type.TAG_String, "sendCommandFeedback", "true"); + NBTTag showDeathMessagesTag = new NBTTag(NBTTag.Type.TAG_String, "showDeathMessages", "true"); + NBTTag spawnRadiusTag = new NBTTag(NBTTag.Type.TAG_String, "spawnRadius", "10"); + NBTTag spectatorsGenerateChunksTag = new NBTTag(NBTTag.Type.TAG_String, "spectatorsGenerateChunks", "true"); + + NBTTag[] ruleList = new NBTTag[] { announceAdvancementsTag, commandBlockOutputTag, disableElytraMovementCheckTag, doDaylightCycleTag, doEntityDropsTag, doFireTickTag, doLimitedCraftingTag, + doMobLootTag, doMobSpawningTag, doTileDropsTag, doWeatherCycleTag, keepInventoryTag, logAdminCommandsTag, maxCommandChainLengthTag, maxEntityCrammingTag, mobGriefingTag, + naturalRegenerationTag, randomTickSpeedTag, reducedDebugInfoTag, sendCommandFeedbackTag, showDeathMessagesTag, spawnRadiusTag, spectatorsGenerateChunksTag, + new NBTTag(NBTTag.Type.TAG_End, null, null) }; + + NBTTag gameRulesTag = new NBTTag(NBTTag.Type.TAG_Compound, "GameRules", ruleList); + + /** + * Version: + */ + + NBTTag versionIdTag = new NBTTag(NBTTag.Type.TAG_Int, "Id", 1631); + NBTTag versionNameTag = new NBTTag(NBTTag.Type.TAG_String, "Name", "1.13.2"); + NBTTag isSnapshotTag = new NBTTag(NBTTag.Type.TAG_Byte, "Snapshot", (byte) 0); + + NBTTag[] versionParameters = new NBTTag[] { versionIdTag, versionNameTag, isSnapshotTag, NBTTag.END_TAG }; + NBTTag version = new NBTTag(NBTTag.Type.TAG_Compound, "Version", versionParameters); + + /** + * Generator options: + */ + + NBTTag structureTag = new NBTTag(NBTTag.Type.TAG_Compound, "structures", new NBTTag[] { NBTTag.END_TAG }); + NBTTag biomeTag = new NBTTag(NBTTag.Type.TAG_String, "biome", "minecraft:plains"); + + NBTTag layers = new NBTTag("layers", NBTTag.Type.TAG_List); + layers.addTag(HELPER.getLayerTagByBlockAndHeight("minecraft:bedrock", (byte) 1)); + layers.addTag(HELPER.getLayerTagByBlockAndHeight("minecraft:dirt", (byte) (world.GROUNDLEVEL - 1))); + layers.addTag(HELPER.getLayerTagByBlockAndHeight("minecraft:grass_block", (byte) 1)); + + NBTTag generatorOptionsTag = new NBTTag(NBTTag.Type.TAG_Compound, "generatorOptions", new NBTTag[] { structureTag, biomeTag, layers, NBTTag.END_TAG }); + + NBTTag[] tagList = new NBTTag[] { versionTag, dataVersionTag,difficultyTag, difficultyLockedTag, borderCenterXTag, initializedTag, clearWeatherTimeTag, borderDamagePerBlockTag, borderSizeLerpTimeTag, + borderCenterZTag, borderWarningBlocksTag, borderSizeLerpTargetTag, borderWarningTimeTag, borderSafeZoneTag, borderSizeTag, levelNameTag, generatorNameTag, generatorVersionTag, + generatorOptionsTag, randomSeedTag, mapFeaturesTag, lastPlayedTag, sizeOnDiskTag, allowCommandsTag, hardcoreTag, gameTypeTag, timeTag, dayTimeTag, spawnXTag, spawnYTag, spawnZTag, rainingTag, + rainTimeTag, thunderingTag, thunderTimeTag, gameRulesTag, version, NBTTag.END_TAG }; + + NBTTag dataTag = new NBTTag(NBTTag.Type.TAG_Compound, "Data", tagList); + return new NBTTag(NBTTag.Type.TAG_Compound, "", new NBTTag[] { dataTag, NBTTag.END_TAG }); + } + public void writeToFile() { try { tag.writeTo(levelFile.getLevelDataOutputStream()); @@ -77,5 +149,5 @@ public void writeToFile() { e.printStackTrace(); } } - + } diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Region.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Region.java index 2ce6fed..279214e 100644 --- a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Region.java +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/Region.java @@ -4,6 +4,8 @@ import java.io.DataOutputStream; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import codemetropolis.blockmodifier.ext.RegionFile; import codemetropolis.blockmodifier.ext.NBTTag; @@ -56,11 +58,13 @@ public static Region loadFromFile(int x, int z, World world) { public void writeToFile() { try { + connectNeighbouringFecnes(); for(int i = 0; i < 1024; i++) { Chunk c = chunks[i]; if(c != null) { DataOutputStream outputStream = regionFile.getChunkDataOutputStream(i % 32, i / 32); c.calculateLighting(); + //c.filterMagicalBlocks(); c.toNBT().writeTo(outputStream); } } @@ -70,6 +74,62 @@ public void writeToFile() { } } + public void connectNeighbouringFecnes() { + for(int x=0; x<512; x++) { + for(int y=0; y<256; y++) { + for(int z=0; z<512; z++) { + String blockId = getBlockIdAt(x, y, z); + + if( blockId.equals("minecraft:oak_fence") ) { + Map fenceProperties = new HashMap<>(); + + if( z > 0 && getBlockIdAt(x, y, z-1).equals("minecraft:oak_fence") ) { + fenceProperties.put("north", "true"); + } + if( x > 0 && getBlockIdAt(x-1, y, z).equals("minecraft:oak_fence") ) { + fenceProperties.put("west", "true"); + } + if( z < 512-1 && getBlockIdAt(x, y, z+1).equals("minecraft:oak_fence") ) { + fenceProperties.put("south", "true"); + } + if( x < 512-1 && getBlockIdAt(x+1, y, z).equals("minecraft:oak_fence") ) { + fenceProperties.put("east", "true"); + } + + if( !fenceProperties.isEmpty() ) { + setBlockAt(x, y, z, "minecraft:oak_fence", fenceProperties); + } + } + } + } + } + } + + public String getBlockIdAt(int x, int y, int z) { + int chunkX = x >> 4; + int chunkZ = z >> 4; + int chunkIndex = chunkZ * 32 + chunkX; + Chunk chunk = chunks[chunkIndex]; + + if( chunk == null ) { + return ""; + } + + int blockX = x & 0xF; + int blockZ = z & 0xF; + return chunk.getBlockId(blockX, y, blockZ); + } + + public void setBlockAt(int x, int y, int z, String blockId, Map blockProperties) { + int chunkX = x >> 4; + int chunkZ = z >> 4; + int chunkIndex = chunkZ * 32 + chunkX; + Chunk chunk = chunks[chunkIndex]; + int blockX = x & 0xF; + int blockZ = z & 0xF; + chunk.setBlock(blockX, y, blockZ, blockId, blockProperties); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/World.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/World.java index 93e62d0..348c0aa 100644 --- a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/World.java +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/World.java @@ -4,8 +4,10 @@ import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.LinkedList; +import java.util.Map; import codemetropolis.blockmodifier.ext.NBTException; +import codemetropolis.blockmodifier.ext.NBTTag; public class World { @@ -27,16 +29,11 @@ public World(String path, int groundLevel) { level.writeToFile(); } - private void setBlock(int x, int y, int z, int type, int data, Object other) { - - if(y < 0 || y > 255) { - try { - throw new NBTException("Block's 'y' coordinate must be between 0 and 255"); - } catch (NBTException e) { - e.printStackTrace(); - } + private void setBlock(int x, int y, int z, String blockId, NBTTag[] blockProperties, Object other) { + if (y < 0 || y > 255) { + throw new IllegalArgumentException("Block's 'y' coordinate must be between 0 and 255"); } - + int regionX = x >> 9; int regionZ = z >> 9; int chunkX = x >> 4; @@ -55,19 +52,19 @@ private void setBlock(int x, int y, int z, int type, int data, Object other) { if(chunk == null) { chunk = new Chunk(chunkX, chunkZ); if(groundBuilding) - chunk.fill(GROUNDLEVEL, (byte) 2); + chunk.fill(GROUNDLEVEL, "minecraft:grass_block", new NBTTag[] {new NBTTag(NBTTag.Type.TAG_String, "snowy", "false"), NBTTag.END_TAG}); region.setChunk(chunkIndexX, chunkIndexZ, chunk); } - chunk.setBlock(blockX, y, blockZ, (byte) type, (byte) data); + chunk.setBlock(blockX, y, blockZ, blockId, blockProperties); - if(type == 63 || type == 68) { + if( "minecraft:sign".equals(blockId) || "minecraft:wall_sign".equals(blockId) ) { chunk.setSignText(x, y, z, (String) other); - } else if (type == 54) { + } else if ( "minecraft:chest".equals(blockId) ) { chunk.clearChestItems(x, y, z); int[] items = (int[])other; for(int i = 0; i < items.length; i += 2) chunk.addChestItem(x, y, z, items[i], items[i+1]); - } else if (type == 176) { + } else if ( "minecraft:standing_banner".equals(blockId) ) { chunk.setBannerColor(x, y, z, (int)other); } else { chunk.clearTileEntitiesAt(x, y, z); @@ -75,44 +72,60 @@ private void setBlock(int x, int y, int z, int type, int data, Object other) { } - public void setBlock(int x, int y, int z, int type, int data) { - setBlock(x, y, z, type, data, null); + public void setBlock(int x, int y, int z, String blockId, NBTTag[] blockProperties) { + setBlock(x, y, z, blockId, blockProperties, null); } - public void setBlock(int x, int y, int z, int type) { - setBlock(x, y, z, type, 0, null); + public void setBlock(int x, int y, int z, String blockId) { + setBlock(x, y, z, blockId, new NBTTag[0], null); } public void removeBlock(int x, int y, int z) { - setBlock(x, y, z, 0); + setBlock(x, y, z, "minecraft:air"); } - public void setSignPost(int x, int y, int z, int data, String text) { - setBlock(x, y, z, 63, data, text); + public void setSignPost(int x, int y, int z, NBTTag[] blockProperties, String text) { + setBlock(x, y, z, "minecraft:sign", blockProperties, text); } public void setSignPost(int x, int y, int z, String text) { - setSignPost(x, y, z, 0, text); + setSignPost(x, y, z, new NBTTag[] {new NBTTag(NBTTag.Type.TAG_String, "rotation", "0"), new NBTTag(NBTTag.Type.TAG_String, "waterlogged", "false"), new NBTTag(NBTTag.Type.TAG_End, null, null)}, text); } - public void setWallSign(int x, int y, int z, int data, String text) { - setBlock(x, y, z, 68, data, text); + public void setWallSign(int x, int y, int z, NBTTag[] blockProperties, String text) { + setBlock(x, y, z, "minecraft:wall_sign", blockProperties, text); } public void setWallSign(int x, int y, int z, String text) { - setWallSign(x, y, z, 0, text); + setWallSign(x, y, z, new NBTTag[] {new NBTTag(NBTTag.Type.TAG_String, "facing", "north"), new NBTTag(NBTTag.Type.TAG_String, "waterlogged", "false"), new NBTTag(NBTTag.Type.TAG_End, null, null)}, text); } - public void setChest(int x, int y, int z, int data, int[] items) { - setBlock(x, y, z, 54, data, items); + public void setChest(int x, int y, int z, NBTTag[] blockProperties, int[] items) { + setBlock(x, y, z, "minecraft:chest", blockProperties, items); } public void setChest(int x, int y, int z, int[] items) { - setChest(x, y, z, 0, items); + setChest(x, y, z, new NBTTag[] {new NBTTag(NBTTag.Type.TAG_String, "facing", "north"), new NBTTag(NBTTag.Type.TAG_String, "single", "single"), new NBTTag(NBTTag.Type.TAG_String, "waterlogged", "false"), new NBTTag(NBTTag.Type.TAG_End, null, null)}, items); + } + + public void setBanner(int x, int y, int z, NBTTag[] blockProperties, BannerColor color) { + setBlock(x, y, z, "minecraft:standing_banner", blockProperties, color.ordinal()); + } + + public void setBlock(int x, int y, int z, String blockId, Map properties) { + setBlock(x, y, z, blockId, Chunk.PropertiesToTags(properties)); + } + + public void setSignPost(int x, int y, int z, Map properties, String text) { + setSignPost(x, y, z, Chunk.PropertiesToTags(properties), text); + } + + public void setWallSign(int x, int y, int z, Map properties, String text) { + setWallSign(x, y, z, Chunk.PropertiesToTags(properties), text); } - public void setBanner(int x, int y, int z, int data, BannerColor color) { - setBlock(x, y, z, 176, data, color.ordinal()); + public void setBanner(int x, int y, int z, Map properties, BannerColor color) { + setBanner(x, y, z, Chunk.PropertiesToTags(properties), color); } private Region getRegion(int x, int z) { diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/FileHandler.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/FileHandler.java index 12b4193..218b024 100644 --- a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/FileHandler.java +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/FileHandler.java @@ -29,6 +29,7 @@ public static void copy(File sourceLocation, File targetLocation, String... igno } } + private static void copyDirectory(File source, File target, String... ignore) throws IOException { if (!target.exists()) { target.mkdirs(); diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/LevelFile.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/LevelFile.java index af74c6b..f719434 100644 --- a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/LevelFile.java +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/LevelFile.java @@ -13,8 +13,8 @@ public LevelFile(File path) { path.getParentFile().mkdirs(); try { file = new RandomAccessFile(path, "rw"); - } catch (IOException e) { - e.printStackTrace(); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException("Path is invalid.", e); } } diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/NBTTag.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/NBTTag.java index d866d95..be3852d 100644 --- a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/NBTTag.java +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/ext/NBTTag.java @@ -7,6 +7,9 @@ import java.io.OutputStream; public class NBTTag { + + public static final NBTTag END_TAG = new NBTTag(Type.TAG_End, null, null); + private final Type type; private Type listType = null; private final String name; @@ -24,7 +27,8 @@ public enum Type { TAG_String, TAG_List, TAG_Compound, - TAG_Int_Array + TAG_Int_Array, + TAG_Long_Array } /** @@ -111,6 +115,10 @@ public NBTTag(Type type, String name, Object value) { if (!(value instanceof int[])) throw new IllegalArgumentException(); break; + case TAG_Long_Array: + if (!(value instanceof long[])) + throw new IllegalArgumentException(); + break; default: throw new IllegalArgumentException(); } @@ -188,6 +196,10 @@ public void setValue(Object newValue) if (!(value instanceof int[])) throw new IllegalArgumentException(); break; + case TAG_Long_Array: + if (!(value instanceof long[])) + throw new IllegalArgumentException(); + break; default: throw new IllegalArgumentException(); } @@ -469,6 +481,13 @@ private void writePayload(DataOutputStream dos) throws IOException { dos.writeInt(ia[qq]); } break; + case TAG_Long_Array: + long[] la = (long[]) value; + dos.writeInt(la.length); + for (int qq = 0; qq < la.length; qq++) { + dos.writeLong(la[qq]); + } + break; } } @@ -493,6 +512,9 @@ private String toString(int indentLevel) { case TAG_Int_Array: sb.append(" [" + ((int[])value).length + " * 4 bytes]"); break; + case TAG_Long_Array: + sb.append(" [" + ((long[])value).length + " * 8 bytes]"); + break; case TAG_List: sb.append(": " + (nestedTags.length) + " entries"); break; diff --git a/project/BlockModifierAPI/src/codemetropolis/blockmodifier/helpers/LevelHelper.java b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/helpers/LevelHelper.java new file mode 100644 index 0000000..8de7f88 --- /dev/null +++ b/project/BlockModifierAPI/src/codemetropolis/blockmodifier/helpers/LevelHelper.java @@ -0,0 +1,25 @@ +package codemetropolis.blockmodifier.helpers; + +import codemetropolis.blockmodifier.ext.NBTTag; + +import java.util.Objects; + +public class LevelHelper { + + /** + * Generates a compound tag that describes one layer. + * + * @param block the layer's desired block + * @param height the desired height + * @return an unnamed TAG_Compound NBT tag, consisting a TAG_String (block) and + * a TAG_Byte (height) + */ + public NBTTag getLayerTagByBlockAndHeight(String block, byte height) { + Objects.requireNonNull(block, "The block cannot be null."); + NBTTag blockTag = new NBTTag(NBTTag.Type.TAG_String, "block", block); + NBTTag heightTag = new NBTTag(NBTTag.Type.TAG_Byte, "height", height); + + return new NBTTag(NBTTag.Type.TAG_Compound, "", new NBTTag[]{blockTag, heightTag, NBTTag.END_TAG}); + } + +}