diff --git a/README.md b/README.md index 0eab3678..1a3c8a06 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,6 @@ CodeMetropolis See your software as never before. Official community page: https://plus.google.com/communities/110235162339639686953 + +BlockModifier-API aviable at: https://github.com/blip24/BlockModifier-API +hh \ No newline at end of file diff --git a/dependencies/codemetropolis/cmblockmodifier/1.0.1/cmblockmodifier-1.0.1.jar b/dependencies/codemetropolis/cmblockmodifier/1.0.1/cmblockmodifier-1.0.1.jar index ad74625a..28ec9035 100644 Binary files a/dependencies/codemetropolis/cmblockmodifier/1.0.1/cmblockmodifier-1.0.1.jar and b/dependencies/codemetropolis/cmblockmodifier/1.0.1/cmblockmodifier-1.0.1.jar differ diff --git a/dependencies/codemetropolis/cmblockmodifier/1.0.1/sav/cmblockmodifier-1.0.1.jar b/dependencies/codemetropolis/cmblockmodifier/1.0.1/sav/cmblockmodifier-1.0.1.jar new file mode 100644 index 00000000..ad74625a Binary files /dev/null and b/dependencies/codemetropolis/cmblockmodifier/1.0.1/sav/cmblockmodifier-1.0.1.jar differ diff --git a/examples/mapping/sonarqube_mapping_example_1_0.xml b/examples/mapping/sonarqube_mapping_example_1_0.xml index 37ed8956..2e165955 100644 --- a/examples/mapping/sonarqube_mapping_example_1_0.xml +++ b/examples/mapping/sonarqube_mapping_example_1_0.xml @@ -1,7 +1,28 @@ - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/mapping/sonarqube_mapping_example_2_0.xml b/examples/mapping/sonarqube_mapping_example_2_0.xml index b26ff456..ee158386 100644 --- a/examples/mapping/sonarqube_mapping_example_2_0.xml +++ b/examples/mapping/sonarqube_mapping_example_2_0.xml @@ -1,5 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/mapping/sonarqube_split_mapping_example_1_0.xml b/examples/mapping/sonarqube_split_mapping_example_1_0.xml index 34f0ce57..a627ce25 100644 --- a/examples/mapping/sonarqube_split_mapping_example_1_0.xml +++ b/examples/mapping/sonarqube_split_mapping_example_1_0.xml @@ -1,7 +1,15 @@ - - + + + + + + + + + + @@ -10,6 +18,19 @@ + + + + + + + + + + + + + diff --git a/examples/mapping/sonarqube_split_mapping_example_2_0.xml b/examples/mapping/sonarqube_split_mapping_example_2_0.xml index 2c007da9..d59cb000 100644 --- a/examples/mapping/sonarqube_split_mapping_example_2_0.xml +++ b/examples/mapping/sonarqube_split_mapping_example_2_0.xml @@ -1,11 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/mapping/sourcemeter_mapping_example_1_0.xml b/examples/mapping/sourcemeter_mapping_example_1_0.xml index e289f3e4..8206a44b 100644 --- a/examples/mapping/sourcemeter_mapping_example_1_0.xml +++ b/examples/mapping/sourcemeter_mapping_example_1_0.xml @@ -4,8 +4,16 @@ - - + + + + + + + + + + @@ -60,6 +68,19 @@ + + + + + + + + + + + + + diff --git a/examples/mapping/sourcemeter_mapping_example_2_0.xml b/examples/mapping/sourcemeter_mapping_example_2_0.xml index efaa60e5..4f51f697 100644 --- a/examples/mapping/sourcemeter_mapping_example_2_0.xml +++ b/examples/mapping/sourcemeter_mapping_example_2_0.xml @@ -3,13 +3,35 @@ - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sources/codemetropolis-toolchain-converter/pom.xml b/sources/codemetropolis-toolchain-converter/pom.xml index 3fc4dfe9..ea95f48b 100644 --- a/sources/codemetropolis-toolchain-converter/pom.xml +++ b/sources/codemetropolis-toolchain-converter/pom.xml @@ -52,17 +52,33 @@ codemetropolis.toolchain codemetropolis-toolchain-commons - 1.4.0 args4j args4j - 2.32 sed graphlib - 1.0 + + + + args4j + args4j + 2.32 + + + sed + graphlib + 1.0 + + + codemetropolis.toolchain + codemetropolis-toolchain-commons + 1.4.0 + + + diff --git a/sources/codemetropolis-toolchain-mapping/.classpath b/sources/codemetropolis-toolchain-mapping/.classpath index e7a868fb..9339420e 100644 --- a/sources/codemetropolis-toolchain-mapping/.classpath +++ b/sources/codemetropolis-toolchain-mapping/.classpath @@ -24,12 +24,12 @@ - + - + diff --git a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java index f14233fd..8f66ddd3 100644 --- a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java +++ b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java @@ -24,8 +24,8 @@ public class Linking { static { SUPPORTED_TARGETS.put(Type.FLOOR, new String[]{"width", "height", "length", "character", "external_character", "torches"}); SUPPORTED_TARGETS.put(Type.CELLAR, new String[]{"width", "height", "length", "character", "external_character", "torches"}); - SUPPORTED_TARGETS.put(Type.GARDEN, new String[]{"tree-ratio", "mushroom-ratio", "flower-ratio"}); - SUPPORTED_TARGETS.put(Type.GROUND, new String[]{}); + SUPPORTED_TARGETS.put(Type.GARDEN, new String[]{"tree-ratio", "mushroom-ratio", "flower-ratio", "pig" , "cow", "rabbit", "chicken", "sheep"}); + SUPPORTED_TARGETS.put(Type.GROUND, new String[]{"biome-id"}); } @XmlAttribute diff --git a/sources/codemetropolis-toolchain-rendering/pom.xml b/sources/codemetropolis-toolchain-rendering/pom.xml index 3bbb1592..e54c7e53 100644 --- a/sources/codemetropolis-toolchain-rendering/pom.xml +++ b/sources/codemetropolis-toolchain-rendering/pom.xml @@ -62,11 +62,6 @@ sed graphlib 1.0 - - - codemetropolis - cmblockmodifier - 1.0.1 org.apache.commons diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java index fad5b7de..1730f6bd 100644 --- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java @@ -64,7 +64,7 @@ public boolean execute(ExecutorArgs args) { return false; } - WorldBuilder worldBuilder = new WorldBuilder(renderingArgs.getWorldPath()); + WorldBuilder worldBuilder = new WorldBuilder(renderingArgs.getWorldPath(), renderingArgs.getInputFile()); for(EventListener listener : listeners) { worldBuilder.addEventListener((ProgressEventListener) listener); } diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Chunk.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Chunk.java new file mode 100644 index 00000000..09a14c0f --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Chunk.java @@ -0,0 +1,404 @@ +package codemetropolis.toolchain.rendering.control; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + + +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; + + } + + + + public Chunk(int x, int z) { + + byte terrainPopulated = 1; + long inhabitedTime = 0L; + long lastUpdate = 0L; + + int[] heightMap = new int[256]; + Arrays.fill(heightMap, 0); + + + 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 inhabitedTimeTag = new NBTTag(NBTTag.Type.TAG_Long, "InhabitedTime", inhabitedTime); + NBTTag lastUpdateTag = new NBTTag(NBTTag.Type.TAG_Long, "LastUpdate", lastUpdate); + 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, lastUpdateTag, sectionsTag, entitiesTag, tileEntitiesTag, heightMapTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + 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) }); + + } + + public static Chunk parseNBT(NBTTag t) { + return new Chunk(t); + } + + public NBTTag toNBT() { + return tag; + } + + public void setBiome(byte biomeId){ + byte[] biomes = new byte[256]; + Arrays.fill(biomes, (byte)biomeId); + NBTTag biomesTag = new NBTTag(NBTTag.Type.TAG_Byte_Array, "Biomes", biomes); + tag.getSubtagByName("Level").addTag(biomesTag); + } + + public void setBlock(int x, int y, int z, byte type, byte data) { + int index = y >> 4; + NBTTag section = getSection(index); + 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); + } else { + value = (byte)((value & 0x0F) | (data << 4)); + } + ((byte[]) section.getSubtagByName("Data").getValue()) [blockIndex / 2] = value; + + int[] heightMap = (int[])tag.getSubtagByName("Level").getSubtagByName("HeightMap").getValue(); + + if(heightMap[z * 16 + x] < y + 1) + heightMap[z * 16 + x] = y + 1; + } + + 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) { + texts[i] = text.substring(0, 14); + text = text.substring(14); + } else if(text.length() > 0) { + texts[i] = text.substring(0); + text = ""; + } else { + 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]); + } + 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 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 tileEntityTag = new NBTTag(NBTTag.Type.TAG_Compound, "", tagList); + + tileEntities.addTag(tileEntityTag); + + } + + public void setMob(double x, double y, double z, String name) { + + NBTTag entities = tag.getSubtagByName("Level").getSubtagByName("Entities"); + + NBTTag xTag = new NBTTag(NBTTag.Type.TAG_Double, "x", x); + NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Double, "y", y); + NBTTag zTag = new NBTTag(NBTTag.Type.TAG_Double, "z", z); + NBTTag pos = new NBTTag(NBTTag.Type.TAG_List, "Pos", new NBTTag[]{xTag, yTag, zTag}); + NBTTag idTag = new NBTTag(NBTTag.Type.TAG_String, "id", name); + NBTTag noAiTag = new NBTTag(NBTTag.Type.TAG_Byte, "NoAI", (byte)0); + NBTTag[] tagList = new NBTTag[] {pos, idTag, noAiTag, new NBTTag(NBTTag.Type.TAG_End, null, null)}; + NBTTag entityTag = new NBTTag(NBTTag.Type.TAG_Compound, "", tagList); + + entities.addTag(entityTag); + + } + + 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") + ) { + 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 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) { + 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)}; + 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(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)}; + 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) + ) { + 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 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) { + tagsToRemoveIndex.add(i); + } else if(((String)t.getSubtagByName("id").getValue()).equals(id)) { + tagsToRemoveIndex.add(i); + break; + } + } + } + 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) { + 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); + + byte[] skyLight = new byte[2048]; + Arrays.fill(skyLight, (byte)255); + + + NBTTag yTag = new NBTTag(NBTTag.Type.TAG_Byte, "Y", (byte)y); + 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 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 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) { + NBTTag section = addSection(y); + section.getSubtagByName("Blocks").setValue(blocks); + section.getSubtagByName("SkyLight").setValue(skyLight); + return section; + } + + public NBTTag addSectionFilled(int y, byte type, int height) { + byte[] blocks = new byte[4096]; + 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; + } else { + blocks[_y * 256 + _z * 16 + _x] = 0; + } + } + } + } + + 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); + } + + public NBTTag addSectionFilled(int y, byte type) { + return addSectionFilled(y, type, 16); + } + + public void fill(int y, byte type) { + + for(int _y = 0; _y < (y >> 4); _y++) { + addSectionFilled(_y, type); + } + addSectionFilled(y >> 4, type, y % 16 + 1); + + } + + public void calculateLighting() { + byte[] lightingObjects = new byte[] {50, 124}; + 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); + } + } + + 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); + } else { + value = (byte)((a[index / 2] & 0x0F) | (value << 4)); + } + + a[index / 2] = value; + } + + @Override + public String toString() { + return tag.toString(); + } + +} diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/FileHandler.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/FileHandler.java new file mode 100644 index 00000000..a134fffa --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/FileHandler.java @@ -0,0 +1,55 @@ +package codemetropolis.toolchain.rendering.control; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class FileHandler { + + public static void copy(String srcPath, String destPath, String... ignore) { + File sourceLocation = new File(srcPath); + File targetLocation = new File(destPath); + copy(sourceLocation, targetLocation, ignore); + } + + public static void copy(File sourceLocation, File targetLocation, String... ignore) { + for(String s : ignore) + if(sourceLocation.getName().equals(s)) return; + try { + if (sourceLocation.isDirectory()) { + copyDirectory(sourceLocation, targetLocation, ignore); + } else { + copyFile(sourceLocation, targetLocation); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void copyDirectory(File source, File target, String... ignore) throws IOException { + if (!target.exists()) { + target.mkdirs(); + } + + for (String f : source.list()) { + copy(new File(source, f), new File(target, f), ignore); + } + } + + private static void copyFile(File source, File target) throws IOException { + try ( + InputStream in = new FileInputStream(source); + OutputStream out = new FileOutputStream(target) + ) { + byte[] buf = new byte[1024]; + int length; + while ((length = in.read(buf)) > 0) { + out.write(buf, 0, length); + } + } + } + +} diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Level.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Level.java new file mode 100644 index 00000000..331bcea0 --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Level.java @@ -0,0 +1,79 @@ +package codemetropolis.toolchain.rendering.control; + +import java.io.File; +import java.io.IOException; + + +public class Level { + + private LevelFile levelFile; + private NBTTag tag; + + 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) { + 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)}); + } + + } + + public void writeToFile() { + try { + tag.writeTo(levelFile.getLevelDataOutputStream()); + levelFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/LevelFile.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/LevelFile.java new file mode 100644 index 00000000..62dcb813 --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/LevelFile.java @@ -0,0 +1,44 @@ +package codemetropolis.toolchain.rendering.control; + +import java.io.*; +import java.util.zip.*; + +public class LevelFile { + + private final File fileName; + private RandomAccessFile file; + + public LevelFile(File path) { + fileName = path; + path.getParentFile().mkdirs(); + try { + file = new RandomAccessFile(path, "rw"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public synchronized DataInputStream getLevelDataInputStream() { + try { + byte[] data = new byte[(int) file.length()]; + file.read(data); + return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(data)))); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public DataOutputStream getLevelDataOutputStream() { + try { + return new DataOutputStream(new GZIPOutputStream(new FileOutputStream(fileName))); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public void close() throws IOException { + file.close(); + } +} \ No newline at end of file diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/NBTException.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/NBTException.java new file mode 100644 index 00000000..ba0c266f --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/NBTException.java @@ -0,0 +1,29 @@ +package codemetropolis.toolchain.rendering.control; + +public class NBTException extends Exception { + + private static final long serialVersionUID = 1L; + + public NBTException() { + super(); + } + + public NBTException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public NBTException(String message, Throwable cause) { + super(message, cause); + } + + public NBTException(String message) { + super(message); + } + + public NBTException(Throwable cause) { + super(cause); + } + + + +} diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/NBTTag.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/NBTTag.java new file mode 100644 index 00000000..75acdd08 --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/NBTTag.java @@ -0,0 +1,522 @@ +package codemetropolis.toolchain.rendering.control; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class NBTTag { + private final Type type; + private Type listType = null; + private final String name; + private Object value; + + public enum Type { + TAG_End, + TAG_Byte, + TAG_Short, + TAG_Int, + TAG_Long, + TAG_Float, + TAG_Double, + TAG_Byte_Array, + TAG_String, + TAG_List, + TAG_Compound, + TAG_Int_Array + } + + /** + * Create a new TAG_List or TAG_Compound NBT tag. + * + * @param type either TAG_List or TAG_Compound + * @param name name for the new tag or null to create an unnamed tag. + * @param value list of tags to add to the new tag. + */ + public NBTTag(Type type, String name, NBTTag[] value) { + this(type, name, (Object) value); + } + + /** + * Create a new TAG_List with an empty list. Use {@link NBTTag#addTag(NBTTag)} to add tags later. + * + * @param name name for this tag or null to create an unnamed tag. + * @param listType type of the elements in this empty list. + */ + public NBTTag(String name, Type listType) { + this(Type.TAG_List, name, listType); + } + + /** + * Create a new NBT tag. + * + * @param type any value from the {@link Type} enum. + * @param name name for the new tag or null to create an unnamed tag. + * @param value an object that fits the tag type or a {@link Type} to create an empty TAG_List with this list type. + */ + public NBTTag(Type type, String name, Object value) { + switch (type) { + case TAG_End: + if (value != null) + throw new IllegalArgumentException(); + break; + case TAG_Byte: + if (!(value instanceof Byte)) + throw new IllegalArgumentException(); + break; + case TAG_Short: + if (!(value instanceof Short)) + throw new IllegalArgumentException(); + break; + case TAG_Int: + if (!(value instanceof Integer)) + throw new IllegalArgumentException(); + break; + case TAG_Long: + if (!(value instanceof Long)) + throw new IllegalArgumentException(); + break; + case TAG_Float: + if (!(value instanceof Float)) + throw new IllegalArgumentException(); + break; + case TAG_Double: + if (!(value instanceof Double)) + throw new IllegalArgumentException(); + break; + case TAG_Byte_Array: + if (!(value instanceof byte[])) + throw new IllegalArgumentException(); + break; + case TAG_String: + if (!(value instanceof String)) + throw new IllegalArgumentException(); + break; + case TAG_List: + if (value instanceof Type) { + this.listType = (Type) value; + value = new NBTTag[0]; + } else { + if (!(value instanceof NBTTag[])) + throw new IllegalArgumentException(); + this.listType = (((NBTTag[]) value)[0]).getType(); + } + break; + case TAG_Compound: + if (!(value instanceof NBTTag[])) + throw new IllegalArgumentException(); + break; + case TAG_Int_Array: + if (!(value instanceof int[])) + throw new IllegalArgumentException(); + break; + default: + throw new IllegalArgumentException(); + } + this.type = type; + this.name = name; + this.value = value; + } + + public Type getType() { + return type; + } + + public String getName() { + return name; + } + + public Object getValue() { + return value; + } + + public void setValue(Object newValue) + { + switch (type) { + case TAG_End: + if (value != null) + throw new IllegalArgumentException(); + break; + case TAG_Byte: + if (!(value instanceof Byte)) + throw new IllegalArgumentException(); + break; + case TAG_Short: + if (!(value instanceof Short)) + throw new IllegalArgumentException(); + break; + case TAG_Int: + if (!(value instanceof Integer)) + throw new IllegalArgumentException(); + break; + case TAG_Long: + if (!(value instanceof Long)) + throw new IllegalArgumentException(); + break; + case TAG_Float: + if (!(value instanceof Float)) + throw new IllegalArgumentException(); + break; + case TAG_Double: + if (!(value instanceof Double)) + throw new IllegalArgumentException(); + break; + case TAG_Byte_Array: + if (!(value instanceof byte[])) + throw new IllegalArgumentException(); + break; + case TAG_String: + if (!(value instanceof String)) + throw new IllegalArgumentException(); + break; + case TAG_List: + if (value instanceof Type) { + this.listType = (Type) value; + value = new NBTTag[0]; + } else { + if (!(value instanceof NBTTag[])) + throw new IllegalArgumentException(); + this.listType = (((NBTTag[]) value)[0]).getType(); + } + break; + case TAG_Compound: + if (!(value instanceof NBTTag[])) + throw new IllegalArgumentException(); + break; + case TAG_Int_Array: + if (!(value instanceof int[])) + throw new IllegalArgumentException(); + break; + default: + throw new IllegalArgumentException(); + } + + value = newValue; + } + + public void clearList() { + value = new NBTTag[0]; + } + + public Type getListType() { + return listType; + } + + /** + * Add a tag to a TAG_List or a TAG_Compound. + */ + public void addTag(NBTTag tag) { + if (type != Type.TAG_List && type != Type.TAG_Compound) + throw new RuntimeException(); + NBTTag[] subtags = (NBTTag[]) value; + + int index = subtags.length; + + //For TAG_Compund entries, we need to add the tag BEFORE the end, + //or the new tag gets placed after the TAG_End, messing up the data. + //TAG_End MUST be kept at the very end of the TAG_Compound. + if(type == Type.TAG_Compound) index--; + insertTag(tag, index); + } + + /** + * Add a tag to a TAG_List or a TAG_Compound at the specified index. + */ + public void insertTag(NBTTag tag, int index) { + if (type != Type.TAG_List && type != Type.TAG_Compound) + throw new RuntimeException(); + NBTTag[] subtags = (NBTTag[]) value; + if (subtags.length > 0) { + if (type == Type.TAG_List && tag.getType() != getListType()) { + throw new IllegalArgumentException(); + } + } else { + if (type == Type.TAG_List) { + listType = tag.getType(); // qqDPS + } + } + if (index > subtags.length) + throw new IndexOutOfBoundsException(); + NBTTag[] newValue = new NBTTag[subtags.length + 1]; + System.arraycopy(subtags, 0, newValue, 0, index); + newValue[index] = tag; + System.arraycopy(subtags, index, newValue, index + 1, subtags.length - index); + value = newValue; + } + + /** + * Remove a tag from a TAG_List or a TAG_Compound at the specified index. + * + * @return the removed tag + */ + public NBTTag removeTag(int index) { + if (type != Type.TAG_List && type != Type.TAG_Compound) + throw new RuntimeException(); + NBTTag[] subtags = (NBTTag[]) value; + NBTTag victim = subtags[index]; + NBTTag[] newValue = new NBTTag[subtags.length - 1]; + System.arraycopy(subtags, 0, newValue, 0, index); + index++; + System.arraycopy(subtags, index, newValue, index - 1, subtags.length - index); + value = newValue; + return victim; + } + + /** + * Remove a tag from a TAG_List or a TAG_Compound. If the tag is not a child of this tag then nested tags are searched. + * + * @param tag tag to look for + */ + public void removeSubtag(NBTTag tag) { + if (type != Type.TAG_List && type != Type.TAG_Compound) + throw new RuntimeException(); + if (tag == null) + return; + NBTTag[] subtags = (NBTTag[]) value; + for (int i = 0; i < subtags.length; i++) { + if (subtags[i] == tag) { + removeTag(i); + return; + } else { + if (subtags[i].type == Type.TAG_List || subtags[i].type == Type.TAG_Compound) { + subtags[i].removeSubtag(tag); + } + } + } + } + + /** + * Find the first nested tag with specified name in a TAG_Compound. + * + * @param name the name to look for. May be null to look for unnamed tags. + * @return the first nested tag that has the specified name. + */ + + public NBTTag getSubtagByName(String name) { + for(NBTTag t : getSubtags()) { + if(name == null || name.equals("")) { + if(t.getName() == null || t.getName().equals("")) return t; + } else { + if(t.getName() != null && t.getName().equals(name)) return t; + } + } + return null; + } + + /** + * Returns an array of all nested tags. + */ + public NBTTag[] getSubtags() { + if (type != Type.TAG_List && type != Type.TAG_Compound) + return null; + return (NBTTag[]) value; + } + + /** + * Read a tag and its nested tags from an InputStream. + * + * @param is stream to read from, like a FileInputStream + * @return NBT tag or structure read from the InputStream + * @throws IOException if there was no valid NBT structure in the InputStream or if another IOException occurred. + */ + public static NBTTag readFrom(InputStream is) throws IOException { + DataInputStream dis = new DataInputStream(is); + byte type = dis.readByte(); + NBTTag tag = null; + + if (type == 0) { + tag = new NBTTag(Type.TAG_End, null, null); + } else { + tag = new NBTTag(Type.values()[type], dis.readUTF(), readPayload(dis, type)); + } + + dis.close(); + + return tag; + } + + private static Object readPayload(DataInputStream dis, byte type) throws IOException { + switch (type) { + case 0: + return null; + case 1: + return dis.readByte(); + case 2: + return dis.readShort(); + case 3: + return dis.readInt(); + case 4: + return dis.readLong(); + case 5: + return dis.readFloat(); + case 6: + return dis.readDouble(); + case 7: + int length = dis.readInt(); + byte[] ba = new byte[length]; + dis.readFully(ba); + return ba; + case 8: + return dis.readUTF(); + case 9: + byte lt = dis.readByte(); + int ll = dis.readInt(); + NBTTag[] lo = new NBTTag[ll]; + for (int i = 0; i < ll; i++) { + lo[i] = new NBTTag(Type.values()[lt], null, readPayload(dis, lt)); + } + if (lo.length == 0) + return Type.values()[lt]; + else + return lo; + case 10: + byte stt; + NBTTag[] tags = new NBTTag[0]; + do { + stt = dis.readByte(); + String name = null; + if (stt != 0) { + name = dis.readUTF(); + } + NBTTag[] newTags = new NBTTag[tags.length + 1]; + System.arraycopy(tags, 0, newTags, 0, tags.length); + newTags[tags.length] = new NBTTag(Type.values()[stt], name, readPayload(dis, stt)); + tags = newTags; } while (stt != 0); + return tags; + case 11: + int len = dis.readInt(); + int[] ia = new int[len]; + for (int qq = 0; qq < len; qq++) { + ia[qq] = dis.readInt(); + } + return ia; + + } + return null; + } + + /** + * Write a tag and its nested tags to an OutputStream. + * + * @param os stream to write to, like a FileOutputStream + * @throws IOException if this is not a valid NBT structure or if any IOException occurred. + */ + public void writeTo(OutputStream os) throws IOException { + DataOutputStream dos = new DataOutputStream(os); + dos.writeByte(type.ordinal()); + if (type != Type.TAG_End) { + if (name != null) { dos.writeUTF(name); } // qqDPS + writePayload(dos); + } + dos.flush(); + dos.close(); + } + + private void writePayload(DataOutputStream dos) throws IOException { + switch (type) { + case TAG_End: + break; + case TAG_Byte: + dos.writeByte((Byte) value); + break; + case TAG_Short: + dos.writeShort((Short) value); + break; + case TAG_Int: + dos.writeInt((Integer) value); + break; + case TAG_Long: + dos.writeLong((Long) value); + break; + case TAG_Float: + dos.writeFloat((Float) value); + break; + case TAG_Double: + dos.writeDouble((Double) value); + break; + case TAG_Byte_Array: + byte[] ba = (byte[]) value; + dos.writeInt(ba.length); + dos.write(ba); + break; + case TAG_String: + dos.writeUTF((String) value); + break; + case TAG_List: + NBTTag[] list = (NBTTag[]) value; + dos.writeByte(getListType().ordinal()); + dos.writeInt(list.length); + for (NBTTag tt : list) { + tt.writePayload(dos); + } + break; + case TAG_Compound: + NBTTag[] subtags = (NBTTag[]) value; + for (NBTTag subtag : subtags) { + Type type = subtag.getType(); + dos.writeByte(type.ordinal()); + if (type != Type.TAG_End) { + dos.writeUTF(subtag.getName()); + subtag.writePayload(dos); + } + } + break; + case TAG_Int_Array: + int[] ia = (int[]) value; + dos.writeInt(ia.length); + for (int qq = 0; qq < ia.length; qq++) { + dos.writeInt(ia[qq]); + } + break; + + } + } + + @Override + public String toString() { + return toString(0); + } + + private String toString(int indentLevel) { + if (type == Type.TAG_End) return ""; + NBTTag[] nestedTags = getSubtags(); + StringBuilder sb = new StringBuilder(); + indent(sb, indentLevel); + sb.append(type.toString() + "("); + if(name != null && !name.equals("")) sb.append("\"" + name + "\""); + sb.append(")"); + switch(type) { + case TAG_Byte_Array: + sb.append(" [" + ((byte[])value).length + " bytes]"); + break; + case TAG_Int_Array: + sb.append(" [" + ((int[])value).length + " * 4 bytes]"); + break; + case TAG_List: + sb.append(": " + (nestedTags.length) + " entries"); + break; + case TAG_Compound: + sb.append(": " + (nestedTags.length - 1) + " entries"); + break; + default: + sb.append(": " + value); + } + sb.append("\n"); + if(type == Type.TAG_Compound || type == Type.TAG_List) { + indent(sb, indentLevel); + sb.append("{\n"); + for(NBTTag t : nestedTags) sb.append(t.toString(indentLevel + 1)); + indent(sb, indentLevel); + sb.append("}\n"); + } + return sb.toString(); + } + + private StringBuilder indent(StringBuilder sb, int indentLevel) { + String indent = " "; + for(int i = 0; i < indentLevel; i++) sb.append(indent); + return sb; + } + +} diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Region.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Region.java new file mode 100644 index 00000000..e282628e --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/Region.java @@ -0,0 +1,89 @@ +package codemetropolis.toolchain.rendering.control; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; + + +public class Region { + + private int x; + private int z; + RegionFile regionFile; + public Chunk[] chunks = new Chunk[1024]; + + public Region(int x, int z, World world) { + + this.x = x; + this.z = z; + File file = new File(String.format("%s/region/r.%d.%d.mca", world.PATH, x, z)); + file.getParentFile().mkdirs(); + this.regionFile = new RegionFile(file); + + try { + + for(int chunkX = 0; chunkX < 32; chunkX++) { + for(int chunkZ = 0; chunkZ < 32; chunkZ++) { + if(regionFile.hasChunk(chunkX, chunkZ)) { + DataInputStream inputStream = regionFile.getChunkDataInputStream(chunkX, chunkZ); + NBTTag chunkTag = NBTTag.readFrom(inputStream); + Chunk chunk = Chunk.parseNBT(chunkTag); + setChunk(chunkX, chunkZ, chunk); + } + } + } + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + public void setChunk(int x, int z, Chunk c) { + chunks[z * 32 + x] = c; + } + + public Chunk getChunk(int x, int z) { + return chunks[z * 32 + x]; + } + + public static Region loadFromFile(int x, int z, World world) { + return new Region(x, z, world); + } + + public void writeToFile() { + try { + 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.toNBT().writeTo(outputStream); + } + } + regionFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for(Chunk c : chunks) { + if(c != null) + sb.append(c.toString()); + } + return sb.toString(); + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + +} diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/RegionFile.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/RegionFile.java new file mode 100644 index 00000000..b829582b --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/RegionFile.java @@ -0,0 +1,375 @@ +package codemetropolis.toolchain.rendering.control; +/* + ** 2011 January 5 + ** + ** The author disclaims copyright to this source code. In place of + ** a legal notice, here is a blessing: + ** + ** May you do good and not evil. + ** May you find forgiveness for yourself and forgive others. + ** May you share freely, never taking more than you give. + **/ + +/* + * 2011 February 16 + * + * This source code is based on the work of Scaevolus (see notice above). + * It has been slightly modified by Mojang AB (constants instead of magic + * numbers, a chunk timestamp header, and auto-formatted according to our + * formatter template). + * + */ + +// Interfaces with region files on the disk + +/* + + Region File Format + + Concept: The minimum unit of storage on hard drives is 4KB. 90% of Minecraft + chunks are smaller than 4KB. 99% are smaller than 8KB. Write a simple + container to store chunks in single files in runs of 4KB sectors. + + Each region file represents a 32x32 group of chunks. The conversion from + chunk number to region number is floor(coord / 32): a chunk at (30, -3) + would be in region (0, -1), and one at (70, -30) would be at (3, -1). + Region files are named "r.x.z.data", where x and z are the region coordinates. + + A region file begins with a 4KB header that describes where chunks are stored + in the file. A 4-byte big-endian integer represents sector offsets and sector + counts. The chunk offset for a chunk (x, z) begins at byte 4*(x+z*32) in the + file. The bottom byte of the chunk offset indicates the number of sectors the + chunk takes up, and the top 3 bytes represent the sector number of the chunk. + Given a chunk offset o, the chunk data begins at byte 4096*(o/256) and takes up + at most 4096*(o%256) bytes. A chunk cannot exceed 1MB in size. If a chunk + offset is 0, the corresponding chunk is not stored in the region file. + + Chunk data begins with a 4-byte big-endian integer representing the chunk data + length in bytes, not counting the length field. The length must be smaller than + 4096 times the number of sectors. The next byte is a version field, to allow + backwards-compatible updates to how chunks are encoded. + + A version of 1 represents a gzipped NBT file. The gzipped data is the chunk + length - 1. + + A version of 2 represents a deflated (zlib compressed) NBT file. The deflated + data is the chunk length - 1. + + */ + +import java.io.*; +import java.util.ArrayList; +import java.util.zip.*; + +public class RegionFile { + + private static final int VERSION_GZIP = 1; + private static final int VERSION_DEFLATE = 2; + + private static final int SECTOR_BYTES = 4096; + private static final int SECTOR_INTS = SECTOR_BYTES / 4; + + static final int CHUNK_HEADER_SIZE = 5; + private static final byte emptySector[] = new byte[4096]; + + private final File fileName; + private RandomAccessFile file; + private final int offsets[]; + private final int chunkTimestamps[]; + private ArrayList sectorFree; + private int sizeDelta; + private long lastModified = 0; + + public RegionFile(File path) { + offsets = new int[SECTOR_INTS]; + chunkTimestamps = new int[SECTOR_INTS]; + + fileName = path; + debugln("REGION LOAD " + fileName); + + sizeDelta = 0; + + try { + if (path.exists()) { + lastModified = path.lastModified(); + } + + file = new RandomAccessFile(path, "rw"); + + if (file.length() < SECTOR_BYTES) { + /* we need to write the chunk offset table */ + for (int i = 0; i < SECTOR_INTS; ++i) { + file.writeInt(0); + } + // write another sector for the timestamp info + for (int i = 0; i < SECTOR_INTS; ++i) { + file.writeInt(0); + } + + sizeDelta += SECTOR_BYTES * 2; + } + + if ((file.length() & 0xfff) != 0) { + /* the file size is not a multiple of 4KB, grow it */ + for (int i = 0; i < (file.length() & 0xfff); ++i) { + file.write((byte) 0); + } + } + + /* set up the available sector map */ + int nSectors = (int) file.length() / SECTOR_BYTES; + sectorFree = new ArrayList(nSectors); + + for (int i = 0; i < nSectors; ++i) { + sectorFree.add(true); + } + + sectorFree.set(0, false); // chunk offset table + sectorFree.set(1, false); // for the last modified info + + file.seek(0); + for (int i = 0; i < SECTOR_INTS; ++i) { + int offset = file.readInt(); + offsets[i] = offset; + if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.size()) { + for (int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) { + sectorFree.set((offset >> 8) + sectorNum, false); + } + } + } + for (int i = 0; i < SECTOR_INTS; ++i) { + int lastModValue = file.readInt(); + chunkTimestamps[i] = lastModValue; + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /* the modification date of the region file when it was first opened */ + public long lastModified() { + return lastModified; + } + + /* gets how much the region file has grown since it was last checked */ + public synchronized int getSizeDelta() { + int ret = sizeDelta; + sizeDelta = 0; + return ret; + } + + // various small debug printing helpers + private void debug(String in) { +// System.out.print(in); + } + + private void debugln(String in) { + debug(in + "\n"); + } + + private void debug(String mode, int x, int z, String in) { + debug("REGION " + mode + " " + fileName.getName() + "[" + x + "," + z + "] = " + in); + } + + private void debug(String mode, int x, int z, int count, String in) { + debug("REGION " + mode + " " + fileName.getName() + "[" + x + "," + z + "] " + count + "B = " + in); + } + + private void debugln(String mode, int x, int z, String in) { + debug(mode, x, z, in + "\n"); + } + + /* + * gets an (uncompressed) stream representing the chunk data returns null if + * the chunk is not found or an error occurs + */ + public synchronized DataInputStream getChunkDataInputStream(int x, int z) { + if (outOfBounds(x, z)) { + debugln("READ", x, z, "out of bounds"); + return null; + } + + try { + int offset = getOffset(x, z); + if (offset == 0) { + // debugln("READ", x, z, "miss"); + return null; + } + + int sectorNumber = offset >> 8; + int numSectors = offset & 0xFF; + + if (sectorNumber + numSectors > sectorFree.size()) { + debugln("READ", x, z, "invalid sector"); + return null; + } + + file.seek(sectorNumber * SECTOR_BYTES); + int length = file.readInt(); + + if (length > SECTOR_BYTES * numSectors) { + debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors); + return null; + } + + byte version = file.readByte(); + if (version == VERSION_GZIP) { + byte[] data = new byte[length - 1]; + file.read(data); + DataInputStream ret = new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(data)))); + // debug("READ", x, z, " = found"); + return ret; + } else if (version == VERSION_DEFLATE) { + byte[] data = new byte[length - 1]; + file.read(data); + DataInputStream ret = new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(data)))); + // debug("READ", x, z, " = found"); + return ret; + } + + debugln("READ", x, z, "unknown version " + version); + return null; + } catch (IOException e) { + debugln("READ", x, z, "exception"); + return null; + } + } + + public DataOutputStream getChunkDataOutputStream(int x, int z) { + if (outOfBounds(x, z)) return null; + + return new DataOutputStream(new DeflaterOutputStream(new ChunkBuffer(x, z))); + } + + /* + * lets chunk writing be multithreaded by not locking the whole file as a + * chunk is serializing -- only writes when serialization is over + */ + class ChunkBuffer extends ByteArrayOutputStream { + private int x, z; + + public ChunkBuffer(int x, int z) { + super(8096); // initialize to 8KB + this.x = x; + this.z = z; + } + + public void close() { + RegionFile.this.write(x, z, buf, count); + } + } + + /* write a chunk at (x,z) with length bytes of data to disk */ + protected synchronized void write(int x, int z, byte[] data, int length) { + try { + int offset = getOffset(x, z); + int sectorNumber = offset >> 8; + int sectorsAllocated = offset & 0xFF; + int sectorsNeeded = (length + CHUNK_HEADER_SIZE) / SECTOR_BYTES + 1; + + // maximum chunk size is 1MB + if (sectorsNeeded >= 256) { + return; + } + + if (sectorNumber != 0 && sectorsAllocated == sectorsNeeded) { + /* we can simply overwrite the old sectors */ + debug("SAVE", x, z, length, "rewrite"); + write(sectorNumber, data, length); + } else { + /* we need to allocate new sectors */ + + /* mark the sectors previously used for this chunk as free */ + for (int i = 0; i < sectorsAllocated; ++i) { + sectorFree.set(sectorNumber + i, true); + } + + /* scan for a free space large enough to store this chunk */ + int runStart = sectorFree.indexOf(true); + int runLength = 0; + if (runStart != -1) { + for (int i = runStart; i < sectorFree.size(); ++i) { + if (runLength != 0) { + if (sectorFree.get(i)) runLength++; + else runLength = 0; + } else if (sectorFree.get(i)) { + runStart = i; + runLength = 1; + } + if (runLength >= sectorsNeeded) { + break; + } + } + } + + if (runLength >= sectorsNeeded) { + /* we found a free space large enough */ + debug("SAVE", x, z, length, "reuse"); + sectorNumber = runStart; + setOffset(x, z, (sectorNumber << 8) | sectorsNeeded); + for (int i = 0; i < sectorsNeeded; ++i) { + sectorFree.set(sectorNumber + i, false); + } + write(sectorNumber, data, length); + } else { + /* + * no free space large enough found -- we need to grow the + * file + */ + debug("SAVE", x, z, length, "grow"); + file.seek(file.length()); + sectorNumber = sectorFree.size(); + for (int i = 0; i < sectorsNeeded; ++i) { + file.write(emptySector); + sectorFree.add(false); + } + sizeDelta += SECTOR_BYTES * sectorsNeeded; + + write(sectorNumber, data, length); + setOffset(x, z, (sectorNumber << 8) | sectorsNeeded); + } + } + setTimestamp(x, z, (int) (System.currentTimeMillis() / 1000L)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /* write a chunk data to the region file at specified sector number */ + private void write(int sectorNumber, byte[] data, int length) throws IOException { + debugln(" " + sectorNumber); + file.seek(sectorNumber * SECTOR_BYTES); + file.writeInt(length + 1); // chunk length + file.writeByte(VERSION_DEFLATE); // chunk version number + file.write(data, 0, length); // chunk data + } + + /* is this an invalid chunk coordinate? */ + private boolean outOfBounds(int x, int z) { + return x < 0 || x >= 32 || z < 0 || z >= 32; + } + + private int getOffset(int x, int z) { + return offsets[x + z * 32]; + } + + public boolean hasChunk(int x, int z) { + return getOffset(x, z) != 0; + } + + private void setOffset(int x, int z, int offset) throws IOException { + offsets[x + z * 32] = offset; + file.seek((x + z * 32) * 4); + file.writeInt(offset); + } + + private void setTimestamp(int x, int z, int value) throws IOException { + chunkTimestamps[x + z * 32] = value; + file.seek(SECTOR_BYTES + (x + z * 32) * 4); + file.writeInt(value); + } + + public void close() throws IOException { + file.close(); + } +} \ No newline at end of file diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/World.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/World.java new file mode 100644 index 00000000..ae4ea9e0 --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/World.java @@ -0,0 +1,228 @@ +package codemetropolis.toolchain.rendering.control; + + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.LinkedList; + + +public class World { + + public final String PATH; + public final String NAME; + public final int GROUNDLEVEL; + public final boolean HASMOB; + public final byte BIOMEID; + private boolean groundBuilding = true; + private int maxLoadedRegions = 1; + private int mobCounter = 0; + public final long mobLimit = 300; + private LinkedList loadedRegions = new LinkedList(); + + public World(String path, int groundLevel, byte id, boolean hasMob) { + + this.PATH = path; + this.GROUNDLEVEL = groundLevel; + this.BIOMEID = id; + this.HASMOB = hasMob; + String[] splitPath = path.split("[/\\\\]"); + this.NAME = splitPath[splitPath.length - 1]; + Level level = new Level(this); + 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(); + } + } + + int regionX = x >> 9; + int regionZ = z >> 9; + int chunkX = x >> 4; + int chunkZ = z >> 4; + int chunkIndexX = (x % 512) >> 4; + int chunkIndexZ = (z % 512) >> 4; + chunkIndexX = chunkIndexX < 0 ? chunkIndexX + 32 : chunkIndexX; + chunkIndexZ = chunkIndexZ < 0 ? chunkIndexZ + 32 : chunkIndexZ; + int blockX = (x % 512) % 16; + int blockZ = (z % 512) % 16; + blockX = x < 0 ? blockX + 15 : blockX; + blockZ = z < 0 ? blockZ + 15 : blockZ; + + Region region = getRegion(regionX, regionZ); + Chunk chunk = region.getChunk(chunkIndexX, chunkIndexZ); + if(chunk == null) { + chunk = new Chunk(chunkX, chunkZ); + if(groundBuilding) + chunk.fill(GROUNDLEVEL, (byte) 2); + region.setChunk(chunkIndexX, chunkIndexZ, chunk); + } + chunk.setBlock(blockX, y, blockZ, (byte) type, (byte) data); + + if(type == 63 || type == 68) { + chunk.setSignText(x, y, z, (String) other); + } else if (type == 54) { + 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) { + chunk.setBannerColor(x, y, z, (int)other); + } else { + chunk.clearTileEntitiesAt(x, y, z); + } + + + + if(HASMOB && type == -1 && mobCounter < mobLimit){ + chunk.setMob(x, y, z, "Pig"); + mobCounter++; + } + + + + } + + 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, int type) { + setBlock(x, y, z, type, 0, null); + } + + public void removeBlock(int x, int y, int z) { + setBlock(x, y, z, 0); + } + + 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, String text) { + setSignPost(x, y, z, 0, 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, String text) { + setWallSign(x, y, z, 0, 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, int[] items) { + setChest(x, y, z, 0, items); + } + + public void setMob(int x, int y, int z, String name){ setBlock(x, y, z, -1, 0, name); } + + public void setBanner(int x, int y, int z, int data, BannerColor color) { + setBlock(x, y, z, 176, data, color.ordinal()); + } + + private Region getRegion(int x, int z) { + + for(Region r : loadedRegions) { + if(r.getX() == x && r.getZ() == z) { + return r; + } + } + + if(loadedRegions.size() >= maxLoadedRegions) { + loadedRegions.removeFirst().writeToFile(); + } + + Region result = Region.loadFromFile(x, z, this); + loadedRegions.add(result); + return result; + } + + public void groundBuildingOn() { + this.groundBuilding = true; + } + + public void groundBuildingOff() { + this.groundBuilding = false; + } + + public void setMaximumNumberOfLoadedRegions(int max) { + this.maxLoadedRegions = max; + } + + public void finish() { + for(Region r : loadedRegions) { + setBiomeForRegion(r); + r.writeToFile(); + } + loadedRegions.clear(); + } + + public void setBiomeForRegion(Region r){ + for(Chunk c : r.chunks){ + if(c != null){ + c.setBiome(BIOMEID); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + File regionDirectory = new File(PATH + "/region"); + for(File f : regionDirectory.listFiles()) { + if(f.getName().matches("r\\.-?[0-9]*\\.-?[0-9]*.mca")) { + String[] parts = f.getName().split("\\."); + Region region = getRegion(Integer.parseInt(parts[1]), Integer.parseInt(parts[2])); + sb.append("** RegionFile: " + f.getName() + " **\n"); + sb.append(region.toString()); + sb.append("\n"); + } + } + return sb.toString(); + } + + public void toNBTFile(String path) { + try { + PrintWriter writer = new PrintWriter(new File(path + ".nbt")); + writer.println(this); + writer.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + + public void toNBTFile() { + toNBTFile(NAME); + } + + public enum BannerColor { + BLACK, + RED, + GREEN, + BROWN, + BLUE, + PURPLE, + TURQUOISE, + LIGHT_GRAY, + GRAY, + PINK, + LIGHT_GREEN, + YELLOW, + LIGHT_BLUE, + LIGHT_PURPLE, + ORANGE, + WHITE; + } + +} diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java index c8b174c2..44cc57ad 100644 --- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java @@ -8,9 +8,9 @@ import java.util.EventListener; import java.util.List; +import codemetropolis.toolchain.rendering.model.primitive.Mob; import org.apache.commons.lang3.time.StopWatch; -import codemetropolis.blockmodifier.World; import codemetropolis.toolchain.commons.cmxml.Buildable; import codemetropolis.toolchain.commons.cmxml.BuildableTree; import codemetropolis.toolchain.commons.cmxml.exceptions.CmxmlReaderException; @@ -33,8 +33,43 @@ public class WorldBuilder { private int count = 0; private int total = 0; - public WorldBuilder(String worldPath) { - world = new World(worldPath, GROUND_LEVEL); + public WorldBuilder(String worldPath, String inputPath) { + BuildableTree buildables = new BuildableTree(); + try { + buildables.loadFromFile(inputPath); + } catch (CmxmlReaderException e) { + e.printStackTrace(); + return; + } + + List grounds = new ArrayList(); + int biomeID=1; + boolean hasMob = false; + for(Buildable b : buildables.getBuildables()) { + switch (b.getType()) { + case GROUND: + if (b.hasAttribute("biome-id")) { + if(Integer.parseInt(b.getAttributeValue("biome-id"))>-1 && Integer.parseInt(b.getAttributeValue("biome-id"))<40){ + biomeID = Integer.parseInt(b.getAttributeValue("biome-id")); + }else{ + try { + throw new NBTException("Biome ID must be between 0 and 39"); + } catch (NBTException e) { + e.printStackTrace(); + } + } + } + break; + case GARDEN: + if(b.hasAttribute("pig")){ + hasMob = true; + } + break; + } + + + } + world = new World(worldPath, GROUND_LEVEL, (byte)biomeID, hasMob); } public void createBuildings(String inputPath) throws BuildingTypeMismatchException{ diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/BasicBlock.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/BasicBlock.java index dc71b59d..77788236 100644 --- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/BasicBlock.java +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/BasicBlock.java @@ -12,6 +12,7 @@ public class BasicBlock { public static final BasicBlock NonBlock; + public static final BasicBlock Mob; public static final Map idToName; public static final Map idToHumanReadableName; public static final Map nameToId; @@ -19,6 +20,7 @@ public class BasicBlock { static { NonBlock = new BasicBlock((short)-1 ); + Mob = new BasicBlock((short)-2 ); idToName = new HashMap(); idToHumanReadableName = new HashMap(); nameToId = new HashMap(); diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Garden.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Garden.java index 88b163b9..89f39599 100644 --- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Garden.java +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Garden.java @@ -8,6 +8,7 @@ import codemetropolis.toolchain.rendering.model.pattern.RandomPattern; import codemetropolis.toolchain.rendering.model.pattern.RepeationPattern; import codemetropolis.toolchain.rendering.model.pattern.YSplitPattern; +import codemetropolis.toolchain.rendering.model.primitive.Mob; import codemetropolis.toolchain.rendering.model.primitive.SignPost; import codemetropolis.toolchain.rendering.model.primitive.SolidBox; import codemetropolis.toolchain.rendering.util.Orientation; @@ -21,8 +22,18 @@ public Garden(Buildable innerBuildable) throws BuildingTypeMismatchException { throw new BuildingTypeMismatchException(innerBuildable.getType(), getClass()); prepareBase(); - prepareDoor(); + /* + * Removed the gates on the + * sides of the gardens + * at the request of the + * project owner, so the + * mobs do not wonder around. + * To re-enable just uncomment + * this. + */ + //prepareDoor(); prepareSigns(); + prepareMobs(); } private void prepareBase( ) { @@ -144,5 +155,8 @@ private void prepareSigns( ) { primitives.add(new SignPost(position.getX(), position.getY() + 2, position.getZ() + size.getZ() - 1, SignPost.Orientation.SOUTHWEST, innerBuildable.getName())); primitives.add(new SignPost(position.getX() + size.getX() - 1, position.getY() + 2, position.getZ() + size.getZ() - 1, SignPost.Orientation.SOUTHEAST, innerBuildable.getName())); } + private void prepareMobs(){ + primitives.add(new Mob(position.getX() + size.getX()/2, position.getY() + 2, position.getZ() + size.getZ()/2, innerBuildable.getName())); + } } diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java index 931fff2c..eb43f353 100644 --- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java @@ -7,7 +7,7 @@ import java.io.IOException; import java.io.PrintWriter; -import codemetropolis.blockmodifier.World; +import codemetropolis.toolchain.rendering.control.World; import codemetropolis.toolchain.commons.cmxml.Point; import codemetropolis.toolchain.rendering.model.BasicBlock; @@ -41,9 +41,12 @@ public void render(World world) { case 176: world.setBanner(position.getX(), position.getY(), position.getZ(), block.getData(), World.BannerColor.valueOf(info.toUpperCase())); break; + case -2: + world.setMob(position.getX(), position.getY(), position.getZ(), block.getName()); default: world.setBlock(position.getX(), position.getY(), position.getZ(), block.getId(), block.getData()); } + } public String toCSV() { diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Mob.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Mob.java new file mode 100644 index 00000000..f8c33c5b --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Mob.java @@ -0,0 +1,30 @@ +package codemetropolis.toolchain.rendering.model.primitive; + +import codemetropolis.toolchain.commons.cmxml.Point; +import codemetropolis.toolchain.rendering.model.BasicBlock; + +import java.io.File; + + +public class Mob implements Primitive{ + + public static final String[] SupportedMobs = {"pig", "cow", "rabbit", "chicken", "sheep"}; + + private Point position; + private String name; + + public Mob(int x, int y, int z, String name){ + super(); + this.position = new Point(x, y, z); + this.name = name; + } + + @Override + public int toCSVFile(File directory){ + new Boxel(new BasicBlock(BasicBlock.Mob), position, name).toCSVFile(directory); + return 1; + } + + @Override + public int getNumberOfBlocks(){ return 1; } +}