mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-28 11:39:07 +00:00
Matter apis
This commit is contained in:
595
src/main/java/com/volmit/iris/util/data/B.java
Normal file
595
src/main/java/com/volmit/iris/util/data/B.java
Normal file
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.data;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Leaves;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class B {
|
||||
private static final Material AIR_MATERIAL = Material.AIR;
|
||||
private static final BlockData AIR = AIR_MATERIAL.createBlockData();
|
||||
private static final KSet<String> nullBlockDataCache = new KSet<>();
|
||||
private static final KSet<String> nullMaterialCache = new KSet<>();
|
||||
private static final KMap<Material, Boolean> solidCache = new KMap<>();
|
||||
private static final KMap<Material, Boolean> updatableCache = new KMap<>();
|
||||
private static final KMap<Material, Boolean> foliageCache = new KMap<>();
|
||||
private static final KMap<Material, Boolean> litCache = new KMap<>();
|
||||
private static final KMap<Material, Boolean> decorantCache = new KMap<>();
|
||||
private static final KMap<Material, Boolean> storageCache = new KMap<>();
|
||||
private static final KMap<Material, Boolean> storageChestCache = new KMap<>();
|
||||
private static final KMap<String, BlockData> blockDataCache = new KMap<>();
|
||||
private static final KMap<String, Material> materialCache = new KMap<>();
|
||||
|
||||
public static boolean isWater(BlockData b) {
|
||||
return b.getMaterial().equals(Material.WATER);
|
||||
}
|
||||
|
||||
public static BlockData getAir() {
|
||||
return AIR;
|
||||
}
|
||||
|
||||
public static Material getMaterial(String bdx) {
|
||||
Material mat = getMaterialOrNull(bdx);
|
||||
|
||||
if (mat != null) {
|
||||
return mat;
|
||||
}
|
||||
|
||||
return AIR_MATERIAL;
|
||||
}
|
||||
|
||||
public static Material getMaterialOrNull(String bdxx) {
|
||||
String bx = bdxx.trim().toUpperCase();
|
||||
|
||||
if (nullMaterialCache.contains(bx)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Material mat = materialCache.get(bx);
|
||||
|
||||
if (mat != null) {
|
||||
return mat;
|
||||
}
|
||||
|
||||
try {
|
||||
Material mm = Material.valueOf(bx);
|
||||
materialCache.put(bx, mm);
|
||||
return mm;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
nullMaterialCache.add(bx);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSolid(BlockData mat) {
|
||||
return isSolid(mat.getMaterial());
|
||||
}
|
||||
|
||||
public static boolean isSolid(Material mat) {
|
||||
Boolean solid = solidCache.get(mat);
|
||||
|
||||
if (solid != null) {
|
||||
return solid;
|
||||
}
|
||||
|
||||
solid = mat.isSolid();
|
||||
solidCache.put(mat, solid);
|
||||
|
||||
return solid;
|
||||
}
|
||||
|
||||
public static BlockData getOrNull(String bdxf) {
|
||||
try {
|
||||
String bd = bdxf.trim();
|
||||
BlockData bdx = parseBlockData(bd);
|
||||
|
||||
if (bdx == null) {
|
||||
Iris.warn("Unknown Block Data '" + bd + "'");
|
||||
return AIR;
|
||||
}
|
||||
|
||||
return bdx;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.warn("Unknown Block Data '" + bdxf + "'");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static BlockData get(String bdxf) {
|
||||
BlockData bd = getOrNull(bdxf);
|
||||
|
||||
if (bd != null) {
|
||||
return bd;
|
||||
}
|
||||
|
||||
return AIR;
|
||||
}
|
||||
|
||||
private static BlockData parseBlockDataOrNull(String ix) {
|
||||
if (nullBlockDataCache.contains(ix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
BlockData bb = blockDataCache.get(ix);
|
||||
|
||||
if (bb != null) {
|
||||
return bb;
|
||||
}
|
||||
BlockData bx = null;
|
||||
|
||||
if (ix.startsWith("oraxen:") && Iris.linkOraxen.supported()) {
|
||||
bx = Iris.linkOraxen.getBlockDataFor(ix.split("\\Q:\\E")[1]);
|
||||
}
|
||||
|
||||
if (bx == null) {
|
||||
bx = Bukkit.createBlockData(ix);
|
||||
}
|
||||
|
||||
if (bx instanceof Leaves) {
|
||||
((Leaves) bx).setPersistent(true);
|
||||
}
|
||||
|
||||
blockDataCache.put(ix, bx);
|
||||
return bx;
|
||||
} catch (Exception e) {
|
||||
//Iris.reportError(e);
|
||||
Iris.debug("Failed to load block \"" + ix + "\"");
|
||||
|
||||
String block = ix.contains(":") ? ix.split(":")[1].toLowerCase() : ix.toLowerCase();
|
||||
String state = block.contains("[") ? block.split("\\[")[1].split("\\]")[0] : "";
|
||||
Map<String, String> stateMap = new HashMap<>();
|
||||
if (!state.equals("")) {
|
||||
Arrays.stream(state.split(",")).forEach(s -> {
|
||||
stateMap.put(s.split("=")[0], s.split("=")[1]);
|
||||
});
|
||||
}
|
||||
block = block.split("\\[")[0];
|
||||
|
||||
switch (block) {
|
||||
case "cauldron" -> block = "water_cauldron"; //Would fail to load if it has a level parameter
|
||||
case "grass_path" -> block = "dirt_path";
|
||||
case "concrete" -> block = "white_concrete";
|
||||
case "wool" -> block = "white_wool";
|
||||
case "beetroots" -> {
|
||||
if (stateMap.containsKey("age")) {
|
||||
String updated = stateMap.get("age");
|
||||
switch (updated) {
|
||||
case "7" -> updated = "3";
|
||||
case "3", "4", "5" -> updated = "2";
|
||||
case "1", "2" -> updated = "1";
|
||||
}
|
||||
stateMap.put("age", updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> newStates = new HashMap<>();
|
||||
for (String key : stateMap.keySet()) { //Iterate through every state and check if its valid
|
||||
try {
|
||||
String newState = block + "[" + key + "=" + stateMap.get(key) + "]";
|
||||
Bukkit.createBlockData(newState);
|
||||
|
||||
//If we get to here, the state is okay so we can use it
|
||||
newStates.put(key, stateMap.get(key));
|
||||
|
||||
} catch (IllegalArgumentException ignored) { }
|
||||
}
|
||||
|
||||
//Combine all the "good" states again
|
||||
state = newStates.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining(","));
|
||||
if (!state.equals("")) state = "[" + state + "]";
|
||||
String newBlock = block + state;
|
||||
Iris.debug("Converting " + ix + " to " + newBlock);
|
||||
|
||||
try {
|
||||
BlockData bd = Bukkit.createBlockData(newBlock);
|
||||
blockDataCache.put(ix, bd);
|
||||
return bd;
|
||||
} catch (Throwable e1) {
|
||||
Iris.reportError(e1);
|
||||
}
|
||||
|
||||
nullBlockDataCache.add(ix);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockData parseBlockData(String ix) {
|
||||
BlockData bd = parseBlockDataOrNull(ix);
|
||||
|
||||
if (bd != null) {
|
||||
return bd;
|
||||
}
|
||||
|
||||
return AIR;
|
||||
}
|
||||
|
||||
public static boolean isStorage(BlockData mat) {
|
||||
Material mm = mat.getMaterial();
|
||||
Boolean f = storageCache.get(mm);
|
||||
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
f = mm.equals(B.getMaterial("CHEST"))
|
||||
|| mm.equals(B.getMaterial("TRAPPED_CHEST"))
|
||||
|| mm.equals(B.getMaterial("SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("WHITE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("ORANGE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("MAGENTA_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("LIGHT_BLUE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("YELLOW_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("LIME_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("PINK_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("GRAY_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("LIGHT_GRAY_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("CYAN_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("PURPLE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BLUE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BROWN_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("GREEN_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("RED_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BLACK_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BARREL"))
|
||||
|| mm.equals(B.getMaterial("DISPENSER"))
|
||||
|| mm.equals(B.getMaterial("DROPPER"))
|
||||
|| mm.equals(B.getMaterial("HOPPER"))
|
||||
|| mm.equals(B.getMaterial("FURNACE"))
|
||||
|| mm.equals(B.getMaterial("BLAST_FURNACE"))
|
||||
|| mm.equals(B.getMaterial("SMOKER"));
|
||||
storageCache.put(mm, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static boolean isStorageChest(BlockData mat) {
|
||||
if (!isStorage(mat)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Material mm = mat.getMaterial();
|
||||
Boolean f = storageChestCache.get(mm);
|
||||
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
f = mm.equals(B.getMaterial("CHEST"))
|
||||
|| mm.equals(B.getMaterial("TRAPPED_CHEST"))
|
||||
|| mm.equals(B.getMaterial("SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("WHITE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("ORANGE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("MAGENTA_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("LIGHT_BLUE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("YELLOW_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("LIME_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("PINK_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("GRAY_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("LIGHT_GRAY_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("CYAN_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("PURPLE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BLUE_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BROWN_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("GREEN_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("RED_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BLACK_SHULKER_BOX"))
|
||||
|| mm.equals(B.getMaterial("BARREL"))
|
||||
|| mm.equals(B.getMaterial("DISPENSER"))
|
||||
|| mm.equals(B.getMaterial("DROPPER"))
|
||||
|| mm.equals(B.getMaterial("HOPPER"));
|
||||
storageChestCache.put(mm, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static boolean isLit(BlockData mat) {
|
||||
Material mm = mat.getMaterial();
|
||||
Boolean f = litCache.get(mm);
|
||||
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
f = mm.equals(B.getMaterial("GLOWSTONE"))
|
||||
|| mm.equals(B.getMaterial("END_ROD"))
|
||||
|| mm.equals(B.getMaterial("SOUL_SAND"))
|
||||
|| mm.equals(B.getMaterial("TORCH"))
|
||||
|| mm.equals(Material.REDSTONE_TORCH)
|
||||
|| mm.equals(B.getMaterial("SOUL_TORCH"))
|
||||
|| mm.equals(Material.REDSTONE_WALL_TORCH)
|
||||
|| mm.equals(Material.WALL_TORCH)
|
||||
|| mm.equals(B.getMaterial("SOUL_WALL_TORCH"))
|
||||
|| mm.equals(B.getMaterial("LANTERN"))
|
||||
|| mm.equals(Material.JACK_O_LANTERN)
|
||||
|| mm.equals(Material.REDSTONE_LAMP)
|
||||
|| mm.equals(Material.MAGMA_BLOCK)
|
||||
|| mm.equals(B.getMaterial("SHROOMLIGHT"))
|
||||
|| mm.equals(B.getMaterial("SEA_LANTERN"))
|
||||
|| mm.equals(B.getMaterial("SOUL_LANTERN"))
|
||||
|| mm.equals(Material.FIRE)
|
||||
|| mm.equals(B.getMaterial("SOUL_FIRE"))
|
||||
|| mm.equals(B.getMaterial("SEA_PICKLE"))
|
||||
|| mm.equals(Material.BREWING_STAND)
|
||||
|| mm.equals(Material.REDSTONE_ORE);
|
||||
litCache.put(mm, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static boolean isUpdatable(BlockData mat) {
|
||||
Boolean u = updatableCache.get(mat.getMaterial());
|
||||
|
||||
if (u != null) {
|
||||
return u;
|
||||
}
|
||||
|
||||
u = isLit(mat) || isStorage(mat);
|
||||
updatableCache.put(mat.getMaterial(), u);
|
||||
return u;
|
||||
}
|
||||
|
||||
public static boolean isFoliage(Material d) {
|
||||
return isFoliage(d.createBlockData());
|
||||
}
|
||||
|
||||
public static boolean isFoliage(BlockData d) {
|
||||
Boolean f = foliageCache.get(d.getMaterial());
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
if (isFluid(d) || isAir(d) || isSolid(d)) {
|
||||
foliageCache.put(d.getMaterial(), false);
|
||||
return false;
|
||||
}
|
||||
|
||||
Material mat = d.getMaterial();
|
||||
f = mat.equals(Material.POPPY)
|
||||
|| mat.equals(Material.DANDELION)
|
||||
|| mat.equals(B.getMaterial("CORNFLOWER"))
|
||||
|| mat.equals(B.getMaterial("SWEET_BERRY_BUSH"))
|
||||
|| mat.equals(B.getMaterial("CRIMSON_ROOTS"))
|
||||
|| mat.equals(B.getMaterial("WARPED_ROOTS"))
|
||||
|| mat.equals(B.getMaterial("NETHER_SPROUTS"))
|
||||
|| mat.equals(B.getMaterial("ALLIUM"))
|
||||
|| mat.equals(B.getMaterial("AZURE_BLUET"))
|
||||
|| mat.equals(B.getMaterial("BLUE_ORCHID"))
|
||||
|| mat.equals(B.getMaterial("POPPY"))
|
||||
|| mat.equals(B.getMaterial("DANDELION"))
|
||||
|| mat.equals(B.getMaterial("OXEYE_DAISY"))
|
||||
|| mat.equals(B.getMaterial("LILY_OF_THE_VALLEY"))
|
||||
|| mat.equals(B.getMaterial("WITHER_ROSE"))
|
||||
|| mat.equals(Material.DARK_OAK_SAPLING)
|
||||
|| mat.equals(Material.ACACIA_SAPLING)
|
||||
|| mat.equals(Material.JUNGLE_SAPLING)
|
||||
|| mat.equals(Material.BIRCH_SAPLING)
|
||||
|| mat.equals(Material.SPRUCE_SAPLING)
|
||||
|| mat.equals(Material.OAK_SAPLING)
|
||||
|| mat.equals(Material.ORANGE_TULIP)
|
||||
|| mat.equals(Material.PINK_TULIP)
|
||||
|| mat.equals(Material.RED_TULIP)
|
||||
|| mat.equals(Material.WHITE_TULIP)
|
||||
|| mat.equals(Material.FERN)
|
||||
|| mat.equals(Material.LARGE_FERN)
|
||||
|| mat.equals(Material.GRASS)
|
||||
|| mat.equals(Material.TALL_GRASS);
|
||||
foliageCache.put(d.getMaterial(), f);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static boolean canPlaceOnto(Material mat, Material onto) {
|
||||
String key = mat.name() + "" + onto.name();
|
||||
|
||||
if (isFoliage(mat)) {
|
||||
if (!isFoliagePlantable(onto)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (onto.equals(Material.AIR) || onto.equals(B.getMaterial("CAVE_AIR")) || onto.equals(B.getMaterial("VOID_AIR"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (onto.equals(Material.GRASS_BLOCK) && mat.equals(Material.DEAD_BUSH)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (onto.equals(Material.DIRT_PATH)) {
|
||||
if (!mat.isSolid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (onto.equals(Material.ACACIA_LEAVES)
|
||||
|| onto.equals(Material.BIRCH_LEAVES)
|
||||
|| onto.equals(Material.DARK_OAK_LEAVES)
|
||||
|| onto.equals(Material.JUNGLE_LEAVES)
|
||||
|| onto.equals(Material.OAK_LEAVES)
|
||||
|| onto.equals(Material.SPRUCE_LEAVES)) {
|
||||
return mat.isSolid();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isDecorant(BlockData m) {
|
||||
Material mm = m.getMaterial();
|
||||
Boolean f = decorantCache.get(mm);
|
||||
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
f = mm.equals(Material.GRASS)
|
||||
|| mm.equals(Material.TALL_GRASS)
|
||||
|| mm.equals(Material.FERN)
|
||||
|| mm.equals(Material.LARGE_FERN)
|
||||
|| mm.equals(B.getMaterial("CORNFLOWER"))
|
||||
|| mm.equals(Material.SUNFLOWER)
|
||||
|| mm.equals(Material.CHORUS_FLOWER)
|
||||
|| mm.equals(Material.POPPY)
|
||||
|| mm.equals(Material.DANDELION)
|
||||
|| mm.equals(Material.OXEYE_DAISY)
|
||||
|| mm.equals(Material.ORANGE_TULIP)
|
||||
|| mm.equals(Material.PINK_TULIP)
|
||||
|| mm.equals(Material.RED_TULIP)
|
||||
|| mm.equals(Material.WHITE_TULIP)
|
||||
|| mm.equals(Material.LILAC)
|
||||
|| mm.equals(Material.DEAD_BUSH)
|
||||
|| mm.equals(B.getMaterial("SWEET_BERRY_BUSH"))
|
||||
|| mm.equals(Material.ROSE_BUSH)
|
||||
|| mm.equals(B.getMaterial("WITHER_ROSE"))
|
||||
|| mm.equals(Material.ALLIUM)
|
||||
|| mm.equals(Material.BLUE_ORCHID)
|
||||
|| mm.equals(B.getMaterial("LILY_OF_THE_VALLEY"))
|
||||
|| mm.equals(B.getMaterial("CRIMSON_FUNGUS"))
|
||||
|| mm.equals(B.getMaterial("WARPED_FUNGUS"))
|
||||
|| mm.equals(Material.RED_MUSHROOM)
|
||||
|| mm.equals(Material.BROWN_MUSHROOM)
|
||||
|| mm.equals(B.getMaterial("CRIMSON_ROOTS"))
|
||||
|| mm.equals(B.getMaterial("AZURE_BLUET"))
|
||||
|| mm.equals(B.getMaterial("WEEPING_VINES"))
|
||||
|| mm.equals(B.getMaterial("WEEPING_VINES_PLANT"))
|
||||
|| mm.equals(B.getMaterial("WARPED_ROOTS"))
|
||||
|| mm.equals(B.getMaterial("NETHER_SPROUTS"))
|
||||
|| mm.equals(B.getMaterial("TWISTING_VINES"))
|
||||
|| mm.equals(B.getMaterial("TWISTING_VINES_PLANT"))
|
||||
|| mm.equals(Material.SUGAR_CANE)
|
||||
|| mm.equals(Material.WHEAT)
|
||||
|| mm.equals(Material.POTATOES)
|
||||
|| mm.equals(Material.CARROTS)
|
||||
|| mm.equals(Material.BEETROOTS)
|
||||
|| mm.equals(Material.NETHER_WART)
|
||||
|| mm.equals(B.getMaterial("SEA_PICKLE"))
|
||||
|| mm.equals(B.getMaterial("SEAGRASS"))
|
||||
|| mm.equals(B.getMaterial("ACACIA_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("BIRCH_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("CRIMSON_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("DARK_OAK_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("JUNGLE_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("OAK_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("POLISHED_BLACKSTONE_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("SPRUCE_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("STONE_BUTTON"))
|
||||
|| mm.equals(B.getMaterial("WARPED_BUTTON"))
|
||||
|| mm.equals(Material.TORCH)
|
||||
|| mm.equals(B.getMaterial("SOUL_TORCH"));
|
||||
decorantCache.put(mm, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static KList<BlockData> get(KList<String> find) {
|
||||
KList<BlockData> b = new KList<>();
|
||||
|
||||
for (String i : find) {
|
||||
BlockData bd = get(i);
|
||||
|
||||
if (bd != null) {
|
||||
b.add(bd);
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
public static boolean isFoliagePlantable(BlockData d) {
|
||||
return d.getMaterial().equals(Material.GRASS_BLOCK)
|
||||
|| d.getMaterial().equals(Material.ROOTED_DIRT)
|
||||
|| d.getMaterial().equals(Material.DIRT)
|
||||
|| d.getMaterial().equals(Material.COARSE_DIRT)
|
||||
|| d.getMaterial().equals(Material.PODZOL);
|
||||
}
|
||||
|
||||
public static boolean isFoliagePlantable(Material d) {
|
||||
return d.equals(Material.GRASS_BLOCK)
|
||||
|| d.equals(Material.DIRT)
|
||||
|| d.equals(Material.ROOTED_DIRT)
|
||||
|| d.equals(Material.COARSE_DIRT)
|
||||
|| d.equals(Material.PODZOL);
|
||||
}
|
||||
|
||||
public static boolean isFluid(BlockData d) {
|
||||
return d.getMaterial().equals(Material.WATER) || d.getMaterial().equals(Material.LAVA);
|
||||
}
|
||||
|
||||
public static boolean isAirOrFluid(BlockData d) {
|
||||
return isAir(d) || isFluid(d);
|
||||
}
|
||||
|
||||
public static boolean isAir(BlockData d) {
|
||||
if (d == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return d.getMaterial().equals(Material.AIR) || d.getMaterial().equals(Material.CAVE_AIR) || d.getMaterial().equals(Material.VOID_AIR);
|
||||
}
|
||||
|
||||
|
||||
public static String[] getBlockTypes() {
|
||||
KList<String> bt = new KList<>();
|
||||
|
||||
for (Material i : Material.values()) {
|
||||
if (i.isBlock()) {
|
||||
String v = i.createBlockData().getAsString(true);
|
||||
|
||||
if (v.contains("[")) {
|
||||
v = v.split("\\Q[\\E")[0];
|
||||
}
|
||||
|
||||
if (v.contains(":")) {
|
||||
v = v.split("\\Q:\\E")[1];
|
||||
}
|
||||
|
||||
bt.add(v);
|
||||
}
|
||||
}
|
||||
|
||||
return bt.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public static String[] getItemTypes() {
|
||||
KList<String> bt = new KList<>();
|
||||
|
||||
for (Material i : Material.values()) {
|
||||
String v = i.name().toLowerCase().trim();
|
||||
bt.add(v);
|
||||
}
|
||||
|
||||
if (Iris.linkOraxen.supported()) {
|
||||
for (String i : Iris.linkOraxen.getItemTypes()) {
|
||||
bt.add("oraxen:" + i);
|
||||
}
|
||||
}
|
||||
|
||||
return bt.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
@@ -18,20 +18,20 @@
|
||||
|
||||
package com.volmit.iris.util.data;
|
||||
|
||||
import com.volmit.iris.engine.object.biome.LoaderBiome;
|
||||
import com.volmit.iris.engine.object.biome.IrisBiome;
|
||||
|
||||
public class BiomeMap {
|
||||
private final LoaderBiome[] height;
|
||||
private final IrisBiome[] height;
|
||||
|
||||
public BiomeMap() {
|
||||
height = new LoaderBiome[256];
|
||||
height = new IrisBiome[256];
|
||||
}
|
||||
|
||||
public void setBiome(int x, int z, LoaderBiome h) {
|
||||
public void setBiome(int x, int z, IrisBiome h) {
|
||||
height[x * 16 + z] = h;
|
||||
}
|
||||
|
||||
public LoaderBiome getBiome(int x, int z) {
|
||||
public IrisBiome getBiome(int x, int z) {
|
||||
return height[x * 16 + z];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,101 +24,67 @@ import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class DataPalette<T> implements Writable {
|
||||
private static final int DEFAULT_BITS_PER_BLOCK = 4;
|
||||
private static final int CAPACITY = 4096;
|
||||
private int bpb;
|
||||
private NibbleArray data;
|
||||
private KList<T> palette;
|
||||
public class DataPalette<T> {
|
||||
private final KList<T> palette;
|
||||
|
||||
public DataPalette(T defaultValue) {
|
||||
palette = new KList<>();
|
||||
bpb = DEFAULT_BITS_PER_BLOCK;
|
||||
data = new NibbleArray(bpb, CAPACITY);
|
||||
data.setAll(Byte.MIN_VALUE);
|
||||
getPaletteId(defaultValue);
|
||||
public DataPalette() {
|
||||
this(new KList<>(16));
|
||||
}
|
||||
|
||||
public abstract T readType(DataInputStream i);
|
||||
|
||||
public abstract void writeType(T t, DataOutputStream o);
|
||||
|
||||
@Override
|
||||
public void write(DataOutputStream o) throws IOException {
|
||||
o.writeByte(bpb + Byte.MIN_VALUE);
|
||||
o.writeByte(palette.size() + Byte.MIN_VALUE);
|
||||
|
||||
for (T i : palette) {
|
||||
writeType(i, o);
|
||||
}
|
||||
|
||||
data.write(o);
|
||||
public DataPalette(KList<T> palette) {
|
||||
this.palette = palette;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInputStream i) throws IOException {
|
||||
bpb = i.readByte() - Byte.MIN_VALUE;
|
||||
palette = new KList<>();
|
||||
int v = i.readByte() - Byte.MIN_VALUE;
|
||||
|
||||
for (int j = 0; j < v; j++) {
|
||||
palette.add(readType(i));
|
||||
}
|
||||
|
||||
data = new NibbleArray(CAPACITY, i);
|
||||
public KList<T> getPalette() {
|
||||
return palette;
|
||||
}
|
||||
|
||||
private final void expand() {
|
||||
if (bpb < 8) {
|
||||
changeBitsPerBlock(bpb + 1);
|
||||
} else {
|
||||
throw new IndexOutOfBoundsException("The Data Palette can only handle at most 256 block types per 16x16x16 region. We cannot use more than 8 bits per block!");
|
||||
public T get(int index)
|
||||
{
|
||||
synchronized (palette)
|
||||
{
|
||||
if(!palette.hasIndex(index))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return palette.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
public final void optimize() {
|
||||
int targetBits = bpb;
|
||||
int needed = palette.size();
|
||||
public int getIndex(T t) {
|
||||
int v = 0;
|
||||
|
||||
for (int i = 1; i < bpb; i++) {
|
||||
if (Math.pow(2, i) > needed) {
|
||||
targetBits = i;
|
||||
break;
|
||||
synchronized (palette) {
|
||||
v = palette.indexOf(t);
|
||||
|
||||
if (v == -1) {
|
||||
v = palette.size();
|
||||
palette.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
changeBitsPerBlock(targetBits);
|
||||
return v;
|
||||
}
|
||||
|
||||
private final void changeBitsPerBlock(int bits) {
|
||||
bpb = bits;
|
||||
data = new NibbleArray(bpb, CAPACITY, data);
|
||||
}
|
||||
public void write(IOAdapter<T> adapter, DataOutputStream dos) throws IOException {
|
||||
synchronized (palette) {
|
||||
dos.writeShort(getPalette().size() + Short.MIN_VALUE);
|
||||
|
||||
public final void set(int x, int y, int z, T d) {
|
||||
data.set(getCoordinateIndex(x, y, z), getPaletteId(d));
|
||||
}
|
||||
|
||||
public final T get(int x, int y, int z) {
|
||||
return palette.get(data.get(getCoordinateIndex(x, y, z)));
|
||||
}
|
||||
|
||||
private final int getPaletteId(T d) {
|
||||
int index = palette.indexOf(d);
|
||||
|
||||
if (index == -1) {
|
||||
index = palette.size();
|
||||
palette.add(d);
|
||||
|
||||
if (palette.size() > Math.pow(2, bpb)) {
|
||||
expand();
|
||||
for (T t : palette) {
|
||||
adapter.write(t, dos);
|
||||
}
|
||||
}
|
||||
|
||||
return index + Byte.MIN_VALUE;
|
||||
}
|
||||
|
||||
private final int getCoordinateIndex(int x, int y, int z) {
|
||||
return y << 8 | z << 4 | x;
|
||||
public static <T> DataPalette<T> getPalette(IOAdapter<T> adapter, DataInputStream din) throws IOException {
|
||||
KList<T> palette = new KList<>();
|
||||
int s = din.readShort() - Short.MIN_VALUE;
|
||||
|
||||
for (int i = 0; i < s; i++) {
|
||||
palette.add(adapter.read(din));
|
||||
}
|
||||
|
||||
return new DataPalette<>(palette);
|
||||
}
|
||||
}
|
||||
|
||||
25
src/main/java/com/volmit/iris/util/data/DataProvider.java
Normal file
25
src/main/java/com/volmit/iris/util/data/DataProvider.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.data;
|
||||
|
||||
import com.volmit.iris.core.project.loader.IrisData;
|
||||
|
||||
public interface DataProvider {
|
||||
IrisData getData();
|
||||
}
|
||||
29
src/main/java/com/volmit/iris/util/data/IOAdapter.java
Normal file
29
src/main/java/com/volmit/iris/util/data/IOAdapter.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.data;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IOAdapter<T> {
|
||||
void write(T t, DataOutputStream dos) throws IOException;
|
||||
|
||||
T read(DataInputStream din) throws IOException;
|
||||
}
|
||||
124
src/main/java/com/volmit/iris/util/data/NibbleDataPalette.java
Normal file
124
src/main/java/com/volmit/iris/util/data/NibbleDataPalette.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.data;
|
||||
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class NibbleDataPalette<T> implements Writable {
|
||||
private static final int DEFAULT_BITS_PER_BLOCK = 4;
|
||||
private static final int CAPACITY = 4096;
|
||||
private int bpb;
|
||||
private NibbleArray data;
|
||||
private KList<T> palette;
|
||||
|
||||
public NibbleDataPalette(T defaultValue) {
|
||||
palette = new KList<>();
|
||||
bpb = DEFAULT_BITS_PER_BLOCK;
|
||||
data = new NibbleArray(bpb, CAPACITY);
|
||||
data.setAll(Byte.MIN_VALUE);
|
||||
getPaletteId(defaultValue);
|
||||
}
|
||||
|
||||
public abstract T readType(DataInputStream i) throws IOException;
|
||||
|
||||
public abstract void writeType(T t, DataOutputStream o) throws IOException;
|
||||
|
||||
@Override
|
||||
public void write(DataOutputStream o) throws IOException {
|
||||
o.writeByte(bpb + Byte.MIN_VALUE);
|
||||
o.writeByte(palette.size() + Byte.MIN_VALUE);
|
||||
|
||||
for (T i : palette) {
|
||||
writeType(i, o);
|
||||
}
|
||||
|
||||
data.write(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInputStream i) throws IOException {
|
||||
bpb = i.readByte() - Byte.MIN_VALUE;
|
||||
palette = new KList<>();
|
||||
int v = i.readByte() - Byte.MIN_VALUE;
|
||||
|
||||
for (int j = 0; j < v; j++) {
|
||||
palette.add(readType(i));
|
||||
}
|
||||
|
||||
data = new NibbleArray(CAPACITY, i);
|
||||
}
|
||||
|
||||
private final void expand() {
|
||||
if (bpb < 8) {
|
||||
changeBitsPerBlock(bpb + 1);
|
||||
} else {
|
||||
throw new IndexOutOfBoundsException("The Data Palette can only handle at most 256 block types per 16x16x16 region. We cannot use more than 8 bits per block!");
|
||||
}
|
||||
}
|
||||
|
||||
public final void optimize() {
|
||||
int targetBits = bpb;
|
||||
int needed = palette.size();
|
||||
|
||||
for (int i = 1; i < bpb; i++) {
|
||||
if (Math.pow(2, i) > needed) {
|
||||
targetBits = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
changeBitsPerBlock(targetBits);
|
||||
}
|
||||
|
||||
private final void changeBitsPerBlock(int bits) {
|
||||
bpb = bits;
|
||||
data = new NibbleArray(bpb, CAPACITY, data);
|
||||
}
|
||||
|
||||
public final void set(int x, int y, int z, T d) {
|
||||
data.set(getCoordinateIndex(x, y, z), getPaletteId(d));
|
||||
}
|
||||
|
||||
public final T get(int x, int y, int z) {
|
||||
return palette.get(data.get(getCoordinateIndex(x, y, z)));
|
||||
}
|
||||
|
||||
private final int getPaletteId(T d) {
|
||||
int index = palette.indexOf(d);
|
||||
|
||||
if (index == -1) {
|
||||
index = palette.size();
|
||||
palette.add(d);
|
||||
|
||||
if (palette.size() > Math.pow(2, bpb)) {
|
||||
expand();
|
||||
}
|
||||
}
|
||||
|
||||
return index + Byte.MIN_VALUE;
|
||||
}
|
||||
|
||||
private final int getCoordinateIndex(int x, int y, int z) {
|
||||
return y << 8 | z << 4 | x;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
package com.volmit.iris.util.hunk.io;
|
||||
|
||||
import com.volmit.iris.engine.data.B;
|
||||
import com.volmit.iris.util.data.B;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
package com.volmit.iris.util.hunk.io;
|
||||
|
||||
import com.volmit.iris.engine.data.IOAdapter;
|
||||
import com.volmit.iris.util.data.IOAdapter;
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.function.Function3;
|
||||
import com.volmit.iris.util.io.CustomOutputStream;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
package com.volmit.iris.util.hunk.io;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.DataPalette;
|
||||
import com.volmit.iris.util.data.DataPalette;
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.function.Function3;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.object.objects.LoaderObject;
|
||||
import com.volmit.iris.engine.object.objects.IrisObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -37,7 +37,7 @@ public class SKConversion {
|
||||
try (ClipboardReader reader = format.getReader(new FileInputStream(in))) {
|
||||
Clipboard clipboard = reader.read();
|
||||
BlockVector3 size = clipboard.getMaximumPoint().subtract(clipboard.getMinimumPoint());
|
||||
LoaderObject o = new LoaderObject(size.getBlockX() + 1, size.getBlockY() + 1, size.getBlockZ() + 1);
|
||||
IrisObject o = new IrisObject(size.getBlockX() + 1, size.getBlockY() + 1, size.getBlockZ() + 1);
|
||||
|
||||
for (int i = clipboard.getMinimumPoint().getBlockX(); i <= clipboard.getMaximumPoint().getBlockX(); i++) {
|
||||
for (int j = clipboard.getMinimumPoint().getBlockY(); j <= clipboard.getMaximumPoint().getBlockY(); j++) {
|
||||
|
||||
245
src/main/java/com/volmit/iris/util/matter/Matter.java
Normal file
245
src/main/java/com/volmit/iris/util/matter/Matter.java
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter;
|
||||
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
import com.volmit.iris.util.math.BlockPosition;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* When Red Matter isn't enough
|
||||
*
|
||||
* UVI width
|
||||
* UVI height
|
||||
* UVI depth
|
||||
* UVI sliceCount
|
||||
* UTF author
|
||||
* UVL createdAt
|
||||
* UVI version
|
||||
* UTF sliceType (canonical class name)
|
||||
* UVI nodeCount (for each slice)
|
||||
* UVI position [(z * w * h) + (y * w) + x]
|
||||
* ??? nodeData
|
||||
*
|
||||
*/
|
||||
public interface Matter {
|
||||
int VERSION = 1;
|
||||
|
||||
/**
|
||||
* Get the header information
|
||||
* @return the header info
|
||||
*/
|
||||
MatterHeader getHeader();
|
||||
|
||||
/**
|
||||
* Get the width of this matter
|
||||
* @return the width
|
||||
*/
|
||||
int getWidth();
|
||||
|
||||
/**
|
||||
* Get the height of this matter
|
||||
* @return the height
|
||||
*/
|
||||
int getHeight();
|
||||
|
||||
/**
|
||||
* Get the depth of this matter
|
||||
* @return the depth
|
||||
*/
|
||||
int getDepth();
|
||||
|
||||
/**
|
||||
* Get the center of this matter
|
||||
* @return the center
|
||||
*/
|
||||
default BlockPosition getCenter()
|
||||
{
|
||||
return new BlockPosition(getCenterX(), getCenterY(), getCenterZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a slice from the given type
|
||||
* @param type the type class
|
||||
* @param matter the matter this slice will go into (size provider)
|
||||
* @param <T> the type
|
||||
* @return the slice (or null if not supported)
|
||||
*/
|
||||
<T> MatterSlice<T> createSlice(Class<T> type, Matter matter);
|
||||
|
||||
/**
|
||||
* Get the size of this matter
|
||||
* @return the size
|
||||
*/
|
||||
default BlockPosition getSize()
|
||||
{
|
||||
return new BlockPosition(getWidth(), getHeight(), getDepth());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the center X of this matter
|
||||
* @return the center X
|
||||
*/
|
||||
default int getCenterX()
|
||||
{
|
||||
return Math.round(getWidth() / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the center Y of this matter
|
||||
* @return the center Y
|
||||
*/
|
||||
default int getCenterY()
|
||||
{
|
||||
return Math.round(getHeight() / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the center Z of this matter
|
||||
* @return the center Z
|
||||
*/
|
||||
default int getCenterZ()
|
||||
{
|
||||
return Math.round(getDepth() / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the slice for the given type
|
||||
* @param t the type class
|
||||
* @param <T> the type
|
||||
* @return the slice or null
|
||||
*/
|
||||
default <T> MatterSlice<T> getSlice(Class<T> t)
|
||||
{
|
||||
return (MatterSlice<T>) getSliceMap().get(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the slice for the given type
|
||||
* @param c the type class
|
||||
* @param <T> the type
|
||||
* @return the deleted slice, or null if it diddn't exist
|
||||
*/
|
||||
default <T> MatterSlice<T> deleteSlice(Class<?> c)
|
||||
{
|
||||
return (MatterSlice<T>) getSliceMap().remove(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a given slice type
|
||||
* @param c the slice type class
|
||||
* @param slice the slice to assign to the type
|
||||
* @param <T> the slice type
|
||||
* @return the overwritten slice if there was an existing slice of that type
|
||||
*/
|
||||
default <T> MatterSlice<T> putSlice(Class<?> c, MatterSlice<T> slice)
|
||||
{
|
||||
return (MatterSlice<T>) getSliceMap().put(c, slice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a slice exists for a given type
|
||||
* @param c the slice class type
|
||||
* @return true if it exists
|
||||
*/
|
||||
default boolean hasSlice(Class<?> c)
|
||||
{
|
||||
return getSlice(c) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all slices
|
||||
*/
|
||||
default void clearSlices()
|
||||
{
|
||||
getSliceMap().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set backing the slice map keys (slice types)
|
||||
* @return the slice types
|
||||
*/
|
||||
default Set<Class<?>> getSliceTypes()
|
||||
{
|
||||
return getSliceMap().keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all slices
|
||||
* @return the real slice map
|
||||
*/
|
||||
Map<Class<?>, MatterSlice<?>> getSliceMap();
|
||||
|
||||
/**
|
||||
* Writes the data to the output stream. The data will be flushed to the provided output
|
||||
* stream however the provided stream will NOT BE CLOSED, so be sure to actually close it
|
||||
* @param out the output stream
|
||||
* @throws IOException shit happens yo
|
||||
*/
|
||||
default void write(OutputStream out) throws IOException
|
||||
{
|
||||
DataOutputStream dos = new DataOutputStream(out);
|
||||
// Write size
|
||||
Varint.writeUnsignedVarInt(getWidth(), dos);
|
||||
Varint.writeUnsignedVarInt(getHeight(), dos);
|
||||
Varint.writeUnsignedVarInt(getDepth(), dos);
|
||||
dos.writeByte(getSliceTypes().size() + Byte.MIN_VALUE);
|
||||
getHeader().write(dos);
|
||||
|
||||
for(Class<?> i : getSliceTypes())
|
||||
{
|
||||
getSlice(i).write(dos);
|
||||
}
|
||||
|
||||
dos.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the input stream into a matter object using a matter factory.
|
||||
* Does not close the input stream. Be a man, close it yourself.
|
||||
* @param in the input stream
|
||||
* @param matterFactory the matter factory (size) -> new MatterImpl(size);
|
||||
* @return the matter object
|
||||
* @throws IOException shit happens yo
|
||||
*/
|
||||
static Matter read(InputStream in, Function<BlockPosition, Matter> matterFactory) throws IOException, ClassNotFoundException {
|
||||
DataInputStream din = new DataInputStream(in);
|
||||
// Read size into new matter object
|
||||
Matter matter = matterFactory.apply(new BlockPosition(
|
||||
Varint.readUnsignedVarInt(din),
|
||||
Varint.readUnsignedVarInt(din),
|
||||
Varint.readUnsignedVarInt(din)));
|
||||
int sliceCount = din.readByte() - Byte.MIN_VALUE;
|
||||
matter.getHeader().read(din);
|
||||
|
||||
while(sliceCount-- > 0)
|
||||
{
|
||||
Class<?> type = Class.forName(din.readUTF());
|
||||
MatterSlice<?> slice = matter.createSlice(type, matter);
|
||||
slice.read(din);
|
||||
matter.putSlice(type, slice);
|
||||
}
|
||||
|
||||
return matter;
|
||||
}
|
||||
}
|
||||
48
src/main/java/com/volmit/iris/util/matter/MatterHeader.java
Normal file
48
src/main/java/com/volmit/iris/util/matter/MatterHeader.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter;
|
||||
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
import com.volmit.iris.util.math.M;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@Data
|
||||
public class MatterHeader {
|
||||
private String author = "anonymous";
|
||||
private long createdAt = M.ms();
|
||||
private int version = Matter.VERSION;
|
||||
|
||||
public void write(DataOutputStream out) throws IOException
|
||||
{
|
||||
out.writeUTF(author);
|
||||
Varint.writeUnsignedVarLong(createdAt, out);
|
||||
Varint.writeUnsignedVarInt(version, out);
|
||||
}
|
||||
|
||||
public void read(DataInputStream din) throws IOException
|
||||
{
|
||||
setAuthor(din.readUTF());
|
||||
setCreatedAt(Varint.readUnsignedVarLong(din));
|
||||
setVersion(Varint.readUnsignedVarInt(din));
|
||||
}
|
||||
}
|
||||
83
src/main/java/com/volmit/iris/util/matter/MatterHunk.java
Normal file
83
src/main/java/com/volmit/iris/util/matter/MatterHunk.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter;
|
||||
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.hunk.storage.StorageHunk;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.function.Consumer4;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings({"DefaultAnnotationParam", "Lombok"})
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class MatterHunk<T> extends StorageHunk<T> implements Hunk<T> {
|
||||
private final Map<Integer, T> data;
|
||||
|
||||
public MatterHunk(int w, int h, int d) {
|
||||
super(w, h, d);
|
||||
data = new KMap<>();
|
||||
}
|
||||
|
||||
public int getCount()
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRaw(int x, int y, int z, T t) {
|
||||
if (t == null) {
|
||||
data.remove(index(x, y, z));
|
||||
return;
|
||||
}
|
||||
|
||||
data.put(index(x, y, z), t);
|
||||
}
|
||||
|
||||
private Integer index(int x, int y, int z) {
|
||||
return (z * getWidth() * getHeight()) + (y * getWidth()) + x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Hunk<T> iterateSync(Consumer4<Integer, Integer, Integer, T> c) {
|
||||
int idx, z;
|
||||
|
||||
for (Map.Entry<Integer, T> g : data.entrySet()) {
|
||||
idx = g.getKey();
|
||||
z = idx / (getWidth() * getHeight());
|
||||
idx -= (z * getWidth() * getHeight());
|
||||
c.accept(idx % getWidth(), idx / getWidth(), z, g.getValue());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void empty(T b) {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getRaw(int x, int y, int z) {
|
||||
return data.get(index(x, y, z));
|
||||
}
|
||||
}
|
||||
71
src/main/java/com/volmit/iris/util/matter/MatterPalette.java
Normal file
71
src/main/java/com/volmit/iris/util/matter/MatterPalette.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter;
|
||||
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.data.DataPalette;
|
||||
import com.volmit.iris.util.data.IOAdapter;
|
||||
import com.volmit.iris.util.data.NibbleDataPalette;
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MatterPalette<T> implements IOAdapter<T> {
|
||||
private final MatterSlice<T> slice;
|
||||
private final DataPalette<T> palette;
|
||||
|
||||
public MatterPalette(MatterSlice<T> slice)
|
||||
{
|
||||
this.slice = slice;
|
||||
palette = new DataPalette<T>();
|
||||
}
|
||||
|
||||
public MatterPalette(MatterSlice<T> slice, DataInputStream din) throws IOException {
|
||||
this.slice = slice;
|
||||
palette = DataPalette.getPalette(this, din);
|
||||
}
|
||||
|
||||
public void writeNode(T t, DataOutputStream dos) throws IOException {
|
||||
Varint.writeUnsignedVarInt(palette.getIndex(t), dos);
|
||||
}
|
||||
|
||||
public T readNode(DataInputStream din) throws IOException {
|
||||
return palette.get(Varint.readUnsignedVarInt(din));
|
||||
}
|
||||
|
||||
public void writePalette(DataOutputStream dos) throws IOException {
|
||||
palette.write(this, dos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(T t, DataOutputStream dos) throws IOException {
|
||||
slice.writeNode(t, dos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(DataInputStream din) throws IOException {
|
||||
return slice.readNode(din);
|
||||
}
|
||||
|
||||
public void assign(T b) {
|
||||
palette.getIndex(b);
|
||||
}
|
||||
}
|
||||
68
src/main/java/com/volmit/iris/util/matter/MatterSlice.java
Normal file
68
src/main/java/com/volmit/iris/util/matter/MatterSlice.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter;
|
||||
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface MatterSlice<T> extends Hunk<T>
|
||||
{
|
||||
Class<T> getType();
|
||||
|
||||
void writeNode(T b, DataOutputStream dos) throws IOException;
|
||||
|
||||
T readNode(DataInputStream din) throws IOException;
|
||||
|
||||
default void write(DataOutputStream dos) throws IOException
|
||||
{
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
dos.writeUTF(getType().getCanonicalName());
|
||||
MatterPalette<T> palette = new MatterPalette<T>(this);
|
||||
iterateSync((x,y,z,b) -> palette.assign(b));
|
||||
palette.writePalette(dos);
|
||||
Varint.writeUnsignedVarInt(((MatterHunk<?>) this).getCount(), dos);
|
||||
iterateSyncIO((x,y,z,b) -> {
|
||||
Varint.writeUnsignedVarInt((z * w * h) + (y * w) + x, dos);
|
||||
palette.writeNode(b, dos);
|
||||
});
|
||||
}
|
||||
|
||||
default void read(DataInputStream din) throws IOException
|
||||
{
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
|
||||
// canonical is read in parent
|
||||
MatterPalette<T> palette = new MatterPalette<T>(this, din);
|
||||
int nodes = Varint.readUnsignedVarInt(din);
|
||||
int[] pos;
|
||||
|
||||
while(nodes-- > 0)
|
||||
{
|
||||
pos = Cache.to3D(Varint.readUnsignedVarInt(din), w, h);
|
||||
setRaw(pos[0], pos[1], pos[2], palette.readNode(din));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BlockMatter extends RawMatter<BlockData>
|
||||
{
|
||||
public BlockMatter(int width, int height, int depth) {
|
||||
super(width, height, depth, BlockData.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNode(BlockData b, DataOutputStream dos) throws IOException {
|
||||
NBTUtil.write(NBTWorld.getCompound(b), dos, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData readNode(DataInputStream din) throws IOException {
|
||||
return NBTWorld.getBlockData((CompoundTag) NBTUtil.read(din, false).getTag());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BooleanMatter extends RawMatter<Boolean>
|
||||
{
|
||||
public BooleanMatter(int width, int height, int depth) {
|
||||
super(width, height, depth, Boolean.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNode(Boolean b, DataOutputStream dos) throws IOException {
|
||||
dos.writeBoolean(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean readNode(DataInputStream din) throws IOException {
|
||||
return din.readBoolean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
|
||||
public class CompoundMatter extends NBTMatter<CompoundTag>
|
||||
{
|
||||
public CompoundMatter(int width, int height, int depth) {
|
||||
super(width, height, depth, CompoundTag.class);
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,27 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Byte</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class ByteTag extends Tag {
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final byte value;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public ByteTag(String name, byte value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
public class IntMatter extends RawMatter<Integer>
|
||||
{
|
||||
public IntMatter(int width, int height, int depth) {
|
||||
super(width, height, depth, Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getValue() {
|
||||
return value;
|
||||
public void writeNode(Integer b, DataOutputStream dos) throws IOException {
|
||||
Varint.writeSignedVarInt(b, dos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Byte" + append + ": " + value;
|
||||
public Integer readNode(DataInputStream din) throws IOException {
|
||||
return Varint.readSignedVarInt(din);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LongMatter extends RawMatter<Long>
|
||||
{
|
||||
public LongMatter(int width, int height, int depth) {
|
||||
super(width, height, depth, Long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNode(Long b, DataOutputStream dos) throws IOException {
|
||||
Varint.writeSignedVarLong(b, dos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long readNode(DataInputStream din) throws IOException {
|
||||
return Varint.readSignedVarLong(din);
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,30 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Long</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class LongTag extends Tag {
|
||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
||||
import com.volmit.iris.util.nbt.io.NamedTag;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final long value;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public LongTag(String name, long value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
public class NBTMatter<T extends Tag<?>> extends RawMatter<T>
|
||||
{
|
||||
public NBTMatter(int width, int height, int depth, Class<T> c) {
|
||||
super(width, height, depth, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getValue() {
|
||||
return value;
|
||||
public void writeNode(T b, DataOutputStream dos) throws IOException {
|
||||
NBTUtil.write(b, dos, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Long" + append + ": " + value;
|
||||
public T readNode(DataInputStream din) throws IOException {
|
||||
return (T) NBTUtil.read(din, false).getTag();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import com.volmit.iris.util.matter.MatterHunk;
|
||||
import com.volmit.iris.util.matter.MatterSlice;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class RawMatter<T> extends MatterHunk<T> implements MatterSlice<T> {
|
||||
@Getter
|
||||
private final Class<T> type;
|
||||
|
||||
public RawMatter(int width, int height, int depth, Class<T> type)
|
||||
{
|
||||
super(width, height, depth);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRaw(int x, int y, int z, T t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getRaw(int x, int y, int z) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void writeNode(T b, DataOutputStream dos) throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract T readNode(DataInputStream din) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import com.volmit.iris.util.matter.MatterSlice;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class StringMatter extends RawMatter<String>
|
||||
{
|
||||
public StringMatter(int width, int height, int depth) {
|
||||
super(width, height, depth, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNode(String b, DataOutputStream dos) throws IOException {
|
||||
dos.writeUTF(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readNode(DataInputStream din) throws IOException {
|
||||
return din.readUTF();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.Deserializer;
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class NBTDeserializer implements Deserializer<NamedTag> {
|
||||
|
||||
private final boolean compressed;
|
||||
|
||||
public NBTDeserializer() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public NBTDeserializer(boolean compressed) {
|
||||
this.compressed = compressed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedTag fromStream(InputStream stream) throws IOException {
|
||||
NBTInputStream nbtIn;
|
||||
if (compressed) {
|
||||
nbtIn = new NBTInputStream(new GZIPInputStream(stream));
|
||||
} else {
|
||||
nbtIn = new NBTInputStream(stream);
|
||||
}
|
||||
return nbtIn.readTag(Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
}
|
||||
155
src/main/java/com/volmit/iris/util/nbt/io/NBTInputStream.java
Normal file
155
src/main/java/com/volmit/iris/util/nbt/io/NBTInputStream.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.ExceptionBiFunction;
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.nbt.tag.*;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NBTInputStream extends DataInputStream implements MaxDepthIO {
|
||||
|
||||
private static final Map<Byte, ExceptionBiFunction<NBTInputStream, Integer, ? extends Tag<?>, IOException>> readers = new HashMap<>();
|
||||
private static final Map<Byte, Class<?>> idClassMapping = new HashMap<>();
|
||||
|
||||
static {
|
||||
put(EndTag.ID, (i, d) -> EndTag.INSTANCE, EndTag.class);
|
||||
put(ByteTag.ID, (i, d) -> readByte(i), ByteTag.class);
|
||||
put(ShortTag.ID, (i, d) -> readShort(i), ShortTag.class);
|
||||
put(IntTag.ID, (i, d) -> readInt(i), IntTag.class);
|
||||
put(LongTag.ID, (i, d) -> readLong(i), LongTag.class);
|
||||
put(FloatTag.ID, (i, d) -> readFloat(i), FloatTag.class);
|
||||
put(DoubleTag.ID, (i, d) -> readDouble(i), DoubleTag.class);
|
||||
put(ByteArrayTag.ID, (i, d) -> readByteArray(i), ByteArrayTag.class);
|
||||
put(StringTag.ID, (i, d) -> readString(i), StringTag.class);
|
||||
put(ListTag.ID, NBTInputStream::readListTag, ListTag.class);
|
||||
put(CompoundTag.ID, NBTInputStream::readCompound, CompoundTag.class);
|
||||
put(IntArrayTag.ID, (i, d) -> readIntArray(i), IntArrayTag.class);
|
||||
put(LongArrayTag.ID, (i, d) -> readLongArray(i), LongArrayTag.class);
|
||||
}
|
||||
|
||||
private static void put(byte id, ExceptionBiFunction<NBTInputStream, Integer, ? extends Tag<?>, IOException> reader, Class<?> clazz) {
|
||||
readers.put(id, reader);
|
||||
idClassMapping.put(id, clazz);
|
||||
}
|
||||
|
||||
public NBTInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public NamedTag readTag(int maxDepth) throws IOException {
|
||||
byte id = readByte();
|
||||
return new NamedTag(readUTF(), readTag(id, maxDepth));
|
||||
}
|
||||
|
||||
public Tag<?> readRawTag(int maxDepth) throws IOException {
|
||||
byte id = readByte();
|
||||
return readTag(id, maxDepth);
|
||||
}
|
||||
|
||||
private Tag<?> readTag(byte type, int maxDepth) throws IOException {
|
||||
ExceptionBiFunction<NBTInputStream, Integer, ? extends Tag<?>, IOException> f;
|
||||
if ((f = readers.get(type)) == null) {
|
||||
throw new IOException("invalid tag id \"" + type + "\"");
|
||||
}
|
||||
return f.accept(this, maxDepth);
|
||||
}
|
||||
|
||||
private static ByteTag readByte(NBTInputStream in) throws IOException {
|
||||
return new ByteTag(in.readByte());
|
||||
}
|
||||
|
||||
private static ShortTag readShort(NBTInputStream in) throws IOException {
|
||||
return new ShortTag(in.readShort());
|
||||
}
|
||||
|
||||
private static IntTag readInt(NBTInputStream in) throws IOException {
|
||||
return new IntTag(in.readInt());
|
||||
}
|
||||
|
||||
private static LongTag readLong(NBTInputStream in) throws IOException {
|
||||
return new LongTag(in.readLong());
|
||||
}
|
||||
|
||||
private static FloatTag readFloat(NBTInputStream in) throws IOException {
|
||||
return new FloatTag(in.readFloat());
|
||||
}
|
||||
|
||||
private static DoubleTag readDouble(NBTInputStream in) throws IOException {
|
||||
return new DoubleTag(in.readDouble());
|
||||
}
|
||||
|
||||
private static StringTag readString(NBTInputStream in) throws IOException {
|
||||
return new StringTag(in.readUTF());
|
||||
}
|
||||
|
||||
private static ByteArrayTag readByteArray(NBTInputStream in) throws IOException {
|
||||
ByteArrayTag bat = new ByteArrayTag(new byte[in.readInt()]);
|
||||
in.readFully(bat.getValue());
|
||||
return bat;
|
||||
}
|
||||
|
||||
private static IntArrayTag readIntArray(NBTInputStream in) throws IOException {
|
||||
int l = in.readInt();
|
||||
int[] data = new int[l];
|
||||
IntArrayTag iat = new IntArrayTag(data);
|
||||
for (int i = 0; i < l; i++) {
|
||||
data[i] = in.readInt();
|
||||
}
|
||||
return iat;
|
||||
}
|
||||
|
||||
private static LongArrayTag readLongArray(NBTInputStream in) throws IOException {
|
||||
int l = in.readInt();
|
||||
long[] data = new long[l];
|
||||
LongArrayTag iat = new LongArrayTag(data);
|
||||
for (int i = 0; i < l; i++) {
|
||||
data[i] = in.readLong();
|
||||
}
|
||||
return iat;
|
||||
}
|
||||
|
||||
private static ListTag<?> readListTag(NBTInputStream in, int maxDepth) throws IOException {
|
||||
byte listType = in.readByte();
|
||||
ListTag<?> list = ListTag.createUnchecked(idClassMapping.get(listType));
|
||||
int length = in.readInt();
|
||||
if (length < 0) {
|
||||
length = 0;
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
list.addUnchecked(in.readTag(listType, in.decrementMaxDepth(maxDepth)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static CompoundTag readCompound(NBTInputStream in, int maxDepth) throws IOException {
|
||||
CompoundTag comp = new CompoundTag();
|
||||
for (int id = in.readByte() & 0xFF; id != 0; id = in.readByte() & 0xFF) {
|
||||
String key = in.readUTF();
|
||||
Tag<?> element = in.readTag((byte) id, in.decrementMaxDepth(maxDepth));
|
||||
comp.put(key, element);
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
160
src/main/java/com/volmit/iris/util/nbt/io/NBTOutputStream.java
Normal file
160
src/main/java/com/volmit/iris/util/nbt/io/NBTOutputStream.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.ExceptionTriConsumer;
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.nbt.tag.*;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NBTOutputStream extends DataOutputStream implements MaxDepthIO {
|
||||
|
||||
private static final Map<Byte, ExceptionTriConsumer<NBTOutputStream, Tag<?>, Integer, IOException>> writers = new HashMap<>();
|
||||
private static final Map<Class<?>, Byte> classIdMapping = new HashMap<>();
|
||||
|
||||
static {
|
||||
put(EndTag.ID, (o, t, d) -> {
|
||||
}, EndTag.class);
|
||||
put(ByteTag.ID, (o, t, d) -> writeByte(o, t), ByteTag.class);
|
||||
put(ShortTag.ID, (o, t, d) -> writeShort(o, t), ShortTag.class);
|
||||
put(IntTag.ID, (o, t, d) -> writeInt(o, t), IntTag.class);
|
||||
put(LongTag.ID, (o, t, d) -> writeLong(o, t), LongTag.class);
|
||||
put(FloatTag.ID, (o, t, d) -> writeFloat(o, t), FloatTag.class);
|
||||
put(DoubleTag.ID, (o, t, d) -> writeDouble(o, t), DoubleTag.class);
|
||||
put(ByteArrayTag.ID, (o, t, d) -> writeByteArray(o, t), ByteArrayTag.class);
|
||||
put(StringTag.ID, (o, t, d) -> writeString(o, t), StringTag.class);
|
||||
put(ListTag.ID, NBTOutputStream::writeList, ListTag.class);
|
||||
put(CompoundTag.ID, NBTOutputStream::writeCompound, CompoundTag.class);
|
||||
put(IntArrayTag.ID, (o, t, d) -> writeIntArray(o, t), IntArrayTag.class);
|
||||
put(LongArrayTag.ID, (o, t, d) -> writeLongArray(o, t), LongArrayTag.class);
|
||||
}
|
||||
|
||||
private static void put(byte id, ExceptionTriConsumer<NBTOutputStream, Tag<?>, Integer, IOException> f, Class<?> clazz) {
|
||||
writers.put(id, f);
|
||||
classIdMapping.put(clazz, id);
|
||||
}
|
||||
|
||||
public NBTOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
public void writeTag(NamedTag tag, int maxDepth) throws IOException {
|
||||
writeByte(tag.getTag().getID());
|
||||
if (tag.getTag().getID() != 0) {
|
||||
writeUTF(tag.getName() == null ? "" : tag.getName());
|
||||
}
|
||||
writeRawTag(tag.getTag(), maxDepth);
|
||||
}
|
||||
|
||||
public void writeTag(Tag<?> tag, int maxDepth) throws IOException {
|
||||
writeByte(tag.getID());
|
||||
if (tag.getID() != 0) {
|
||||
writeUTF("");
|
||||
}
|
||||
writeRawTag(tag, maxDepth);
|
||||
}
|
||||
|
||||
public void writeRawTag(Tag<?> tag, int maxDepth) throws IOException {
|
||||
ExceptionTriConsumer<NBTOutputStream, Tag<?>, Integer, IOException> f;
|
||||
if ((f = writers.get(tag.getID())) == null) {
|
||||
throw new IOException("invalid tag \"" + tag.getID() + "\"");
|
||||
}
|
||||
f.accept(this, tag, maxDepth);
|
||||
}
|
||||
|
||||
static byte idFromClass(Class<?> clazz) {
|
||||
Byte id = classIdMapping.get(clazz);
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("unknown Tag class " + clazz.getName());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private static void writeByte(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeByte(((ByteTag) tag).asByte());
|
||||
}
|
||||
|
||||
private static void writeShort(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeShort(((ShortTag) tag).asShort());
|
||||
}
|
||||
|
||||
private static void writeInt(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((IntTag) tag).asInt());
|
||||
}
|
||||
|
||||
private static void writeLong(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeLong(((LongTag) tag).asLong());
|
||||
}
|
||||
|
||||
private static void writeFloat(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeFloat(((FloatTag) tag).asFloat());
|
||||
}
|
||||
|
||||
private static void writeDouble(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeDouble(((DoubleTag) tag).asDouble());
|
||||
}
|
||||
|
||||
private static void writeString(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeUTF(((StringTag) tag).getValue());
|
||||
}
|
||||
|
||||
private static void writeByteArray(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((ByteArrayTag) tag).length());
|
||||
out.write(((ByteArrayTag) tag).getValue());
|
||||
}
|
||||
|
||||
private static void writeIntArray(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((IntArrayTag) tag).length());
|
||||
for (int i : ((IntArrayTag) tag).getValue()) {
|
||||
out.writeInt(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLongArray(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((LongArrayTag) tag).length());
|
||||
for (long l : ((LongArrayTag) tag).getValue()) {
|
||||
out.writeLong(l);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeList(NBTOutputStream out, Tag<?> tag, int maxDepth) throws IOException {
|
||||
out.writeByte(idFromClass(((ListTag<?>) tag).getTypeClass()));
|
||||
out.writeInt(((ListTag<?>) tag).size());
|
||||
for (Tag<?> t : ((ListTag<?>) tag)) {
|
||||
out.writeRawTag(t, out.decrementMaxDepth(maxDepth));
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeCompound(NBTOutputStream out, Tag<?> tag, int maxDepth) throws IOException {
|
||||
for (Map.Entry<String, Tag<?>> entry : (CompoundTag) tag) {
|
||||
if (entry.getValue().getID() == 0) {
|
||||
throw new IOException("end tag not allowed");
|
||||
}
|
||||
out.writeByte(entry.getValue().getID());
|
||||
out.writeUTF(entry.getKey());
|
||||
out.writeRawTag(entry.getValue(), out.decrementMaxDepth(maxDepth));
|
||||
}
|
||||
out.writeByte(0);
|
||||
}
|
||||
}
|
||||
51
src/main/java/com/volmit/iris/util/nbt/io/NBTSerializer.java
Normal file
51
src/main/java/com/volmit/iris/util/nbt/io/NBTSerializer.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.Serializer;
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class NBTSerializer implements Serializer<NamedTag> {
|
||||
|
||||
private final boolean compressed;
|
||||
|
||||
public NBTSerializer() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public NBTSerializer(boolean compressed) {
|
||||
this.compressed = compressed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toStream(NamedTag object, OutputStream out) throws IOException {
|
||||
NBTOutputStream nbtOut;
|
||||
if (compressed) {
|
||||
nbtOut = new NBTOutputStream(new GZIPOutputStream(out, true));
|
||||
} else {
|
||||
nbtOut = new NBTOutputStream(out);
|
||||
}
|
||||
nbtOut.writeTag(object, Tag.DEFAULT_MAX_DEPTH);
|
||||
nbtOut.flush();
|
||||
}
|
||||
}
|
||||
108
src/main/java/com/volmit/iris/util/nbt/io/NBTUtil.java
Normal file
108
src/main/java/com/volmit/iris/util/nbt/io/NBTUtil.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
import javax.inject.Named;
|
||||
import java.io.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public final class NBTUtil {
|
||||
|
||||
private NBTUtil() {
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, File file, boolean compressed) throws IOException {
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
new NBTSerializer(compressed).toStream(tag, fos);
|
||||
}
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, OutputStream out, boolean compressed) throws IOException {
|
||||
new NBTSerializer(compressed).toStream(tag, out);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, OutputStream out, boolean compressed) throws IOException {
|
||||
write(new NamedTag(null, tag), out, compressed);
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, String file, boolean compressed) throws IOException {
|
||||
write(tag, new File(file), compressed);
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, File file) throws IOException {
|
||||
write(tag, file, true);
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, String file) throws IOException {
|
||||
write(tag, new File(file), true);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, File file, boolean compressed) throws IOException {
|
||||
write(new NamedTag(null, tag), file, compressed);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, String file, boolean compressed) throws IOException {
|
||||
write(new NamedTag(null, tag), new File(file), compressed);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, File file) throws IOException {
|
||||
write(new NamedTag(null, tag), file, true);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, String file) throws IOException {
|
||||
write(new NamedTag(null, tag), new File(file), true);
|
||||
}
|
||||
|
||||
public static NamedTag read(File file, boolean compressed) throws IOException {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
return new NBTDeserializer(compressed).fromStream(fis);
|
||||
}
|
||||
}
|
||||
|
||||
public static NamedTag read(InputStream in, boolean compressed) throws IOException {
|
||||
return new NBTDeserializer(compressed).fromStream(in);
|
||||
}
|
||||
|
||||
public static NamedTag read(String file, boolean compressed) throws IOException {
|
||||
return read(new File(file), compressed);
|
||||
}
|
||||
|
||||
public static NamedTag read(File file) throws IOException {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
return new NBTDeserializer(false).fromStream(detectDecompression(fis));
|
||||
}
|
||||
}
|
||||
|
||||
public static NamedTag read(String file) throws IOException {
|
||||
return read(new File(file));
|
||||
}
|
||||
|
||||
private static InputStream detectDecompression(InputStream is) throws IOException {
|
||||
PushbackInputStream pbis = new PushbackInputStream(is, 2);
|
||||
int signature = (pbis.read() & 0xFF) + (pbis.read() << 8);
|
||||
pbis.unread(signature >> 8);
|
||||
pbis.unread(signature & 0xFF);
|
||||
if (signature == GZIPInputStream.GZIP_MAGIC) {
|
||||
return new GZIPInputStream(pbis);
|
||||
}
|
||||
return pbis;
|
||||
}
|
||||
}
|
||||
@@ -16,43 +16,33 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
/**
|
||||
* Represents a single NBT tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public abstract class Tag {
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
/**
|
||||
* The name of this tag.
|
||||
*/
|
||||
private final String name;
|
||||
public class NamedTag {
|
||||
|
||||
/**
|
||||
* Creates the tag with the specified name.
|
||||
*
|
||||
* @param name The name.
|
||||
*/
|
||||
public Tag(String name) {
|
||||
private String name;
|
||||
private Tag<?> tag;
|
||||
|
||||
public NamedTag(String name, Tag<?> tag) {
|
||||
this.name = name;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this tag.
|
||||
*
|
||||
* @return The name of this tag.
|
||||
*/
|
||||
public final String getName() {
|
||||
public void setTag(Tag<?> tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of this tag.
|
||||
*
|
||||
* @return The value of this tag.
|
||||
*/
|
||||
public abstract Object getValue();
|
||||
|
||||
public Tag<?> getTag() {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ParseException extends IOException {
|
||||
|
||||
public ParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ParseException(String msg, String value, int index) {
|
||||
super(msg + " at: " + formatError(value, index));
|
||||
}
|
||||
|
||||
private static String formatError(String value, int index) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int i = Math.min(value.length(), index);
|
||||
if (i > 35) {
|
||||
builder.append("...");
|
||||
}
|
||||
builder.append(value, Math.max(0, i - 35), i);
|
||||
builder.append("<--[HERE]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.StringDeserializer;
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SNBTDeserializer implements StringDeserializer<Tag<?>> {
|
||||
|
||||
@Override
|
||||
public Tag<?> fromReader(Reader reader) throws IOException {
|
||||
return fromReader(reader, Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
public Tag<?> fromReader(Reader reader, int maxDepth) throws IOException {
|
||||
BufferedReader bufferedReader;
|
||||
if (reader instanceof BufferedReader) {
|
||||
bufferedReader = (BufferedReader) reader;
|
||||
} else {
|
||||
bufferedReader = new BufferedReader(reader);
|
||||
}
|
||||
return SNBTParser.parse(bufferedReader.lines().collect(Collectors.joining()), maxDepth);
|
||||
}
|
||||
}
|
||||
258
src/main/java/com/volmit/iris/util/nbt/io/SNBTParser.java
Normal file
258
src/main/java/com/volmit/iris/util/nbt/io/SNBTParser.java
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.nbt.tag.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class SNBTParser implements MaxDepthIO {
|
||||
|
||||
private static final Pattern
|
||||
FLOAT_LITERAL_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.?|\\d*\\.\\d+)(?:e[-+]?\\d+)?f$", Pattern.CASE_INSENSITIVE),
|
||||
DOUBLE_LITERAL_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.?|\\d*\\.\\d+)(?:e[-+]?\\d+)?d$", Pattern.CASE_INSENSITIVE),
|
||||
DOUBLE_LITERAL_NO_SUFFIX_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.|\\d*\\.\\d+)(?:e[-+]?\\d+)?$", Pattern.CASE_INSENSITIVE),
|
||||
BYTE_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+b$", Pattern.CASE_INSENSITIVE),
|
||||
SHORT_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+s$", Pattern.CASE_INSENSITIVE),
|
||||
INT_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+$", Pattern.CASE_INSENSITIVE),
|
||||
LONG_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+l$", Pattern.CASE_INSENSITIVE),
|
||||
NUMBER_PATTERN = Pattern.compile("^[-+]?\\d+$");
|
||||
|
||||
private final StringPointer ptr;
|
||||
|
||||
private SNBTParser(String string) {
|
||||
this.ptr = new StringPointer(string);
|
||||
}
|
||||
|
||||
public static Tag<?> parse(String string, int maxDepth) throws ParseException {
|
||||
SNBTParser parser = new SNBTParser(string);
|
||||
Tag<?> tag = parser.parseAnything(maxDepth);
|
||||
parser.ptr.skipWhitespace();
|
||||
if (parser.ptr.hasNext()) {
|
||||
throw parser.ptr.parseException("invalid characters after end of snbt");
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static Tag<?> parse(String string) throws ParseException {
|
||||
return parse(string, Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
private Tag<?> parseAnything(int maxDepth) throws ParseException {
|
||||
ptr.skipWhitespace();
|
||||
switch (ptr.currentChar()) {
|
||||
case '{':
|
||||
return parseCompoundTag(maxDepth);
|
||||
case '[':
|
||||
if (ptr.hasCharsLeft(2) && ptr.lookAhead(1) != '"' && ptr.lookAhead(2) == ';') {
|
||||
return parseNumArray();
|
||||
}
|
||||
return parseListTag(maxDepth);
|
||||
}
|
||||
return parseStringOrLiteral();
|
||||
}
|
||||
|
||||
private Tag<?> parseStringOrLiteral() throws ParseException {
|
||||
ptr.skipWhitespace();
|
||||
if (ptr.currentChar() == '"') {
|
||||
return new StringTag(ptr.parseQuotedString());
|
||||
}
|
||||
String s = ptr.parseSimpleString();
|
||||
if (s.isEmpty()) {
|
||||
throw new ParseException("expected non empty value");
|
||||
}
|
||||
if (FLOAT_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
return new FloatTag(Float.parseFloat(s.substring(0, s.length() - 1)));
|
||||
} else if (BYTE_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new ByteTag(Byte.parseByte(s.substring(0, s.length() - 1)));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("byte not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (SHORT_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new ShortTag(Short.parseShort(s.substring(0, s.length() - 1)));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("short not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (LONG_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new LongTag(Long.parseLong(s.substring(0, s.length() - 1)));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("long not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (INT_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new IntTag(Integer.parseInt(s));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("int not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (DOUBLE_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
return new DoubleTag(Double.parseDouble(s.substring(0, s.length() - 1)));
|
||||
} else if (DOUBLE_LITERAL_NO_SUFFIX_PATTERN.matcher(s).matches()) {
|
||||
return new DoubleTag(Double.parseDouble(s));
|
||||
} else if ("true".equalsIgnoreCase(s)) {
|
||||
return new ByteTag(true);
|
||||
} else if ("false".equalsIgnoreCase(s)) {
|
||||
return new ByteTag(false);
|
||||
}
|
||||
return new StringTag(s);
|
||||
}
|
||||
|
||||
private CompoundTag parseCompoundTag(int maxDepth) throws ParseException {
|
||||
ptr.expectChar('{');
|
||||
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
|
||||
ptr.skipWhitespace();
|
||||
while (ptr.hasNext() && ptr.currentChar() != '}') {
|
||||
ptr.skipWhitespace();
|
||||
String key = ptr.currentChar() == '"' ? ptr.parseQuotedString() : ptr.parseSimpleString();
|
||||
if (key.isEmpty()) {
|
||||
throw new ParseException("empty keys are not allowed");
|
||||
}
|
||||
ptr.expectChar(':');
|
||||
|
||||
compoundTag.put(key, parseAnything(decrementMaxDepth(maxDepth)));
|
||||
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar('}');
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
private ListTag<?> parseListTag(int maxDepth) throws ParseException {
|
||||
ptr.expectChar('[');
|
||||
ptr.skipWhitespace();
|
||||
ListTag<?> list = ListTag.createUnchecked(EndTag.class);
|
||||
while (ptr.currentChar() != ']') {
|
||||
Tag<?> element = parseAnything(decrementMaxDepth(maxDepth));
|
||||
try {
|
||||
list.addUnchecked(element);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException(ex.getMessage());
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
return list;
|
||||
}
|
||||
|
||||
private ArrayTag<?> parseNumArray() throws ParseException {
|
||||
ptr.expectChar('[');
|
||||
char arrayType = ptr.next();
|
||||
ptr.expectChar(';');
|
||||
ptr.skipWhitespace();
|
||||
switch (arrayType) {
|
||||
case 'B':
|
||||
return parseByteArrayTag();
|
||||
case 'I':
|
||||
return parseIntArrayTag();
|
||||
case 'L':
|
||||
return parseLongArrayTag();
|
||||
}
|
||||
throw new ParseException("invalid array type '" + arrayType + "'");
|
||||
}
|
||||
|
||||
private ByteArrayTag parseByteArrayTag() throws ParseException {
|
||||
List<Byte> byteList = new ArrayList<>();
|
||||
while (ptr.currentChar() != ']') {
|
||||
String s = ptr.parseSimpleString();
|
||||
ptr.skipWhitespace();
|
||||
if (NUMBER_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
byteList.add(Byte.parseByte(s));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("byte not in range: \"" + s + "\"");
|
||||
}
|
||||
} else {
|
||||
throw ptr.parseException("invalid byte in ByteArrayTag: \"" + s + "\"");
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
byte[] bytes = new byte[byteList.size()];
|
||||
for (int i = 0; i < byteList.size(); i++) {
|
||||
bytes[i] = byteList.get(i);
|
||||
}
|
||||
return new ByteArrayTag(bytes);
|
||||
}
|
||||
|
||||
private IntArrayTag parseIntArrayTag() throws ParseException {
|
||||
List<Integer> intList = new ArrayList<>();
|
||||
while (ptr.currentChar() != ']') {
|
||||
String s = ptr.parseSimpleString();
|
||||
ptr.skipWhitespace();
|
||||
if (NUMBER_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
intList.add(Integer.parseInt(s));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("int not in range: \"" + s + "\"");
|
||||
}
|
||||
} else {
|
||||
throw ptr.parseException("invalid int in IntArrayTag: \"" + s + "\"");
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
return new IntArrayTag(intList.stream().mapToInt(i -> i).toArray());
|
||||
}
|
||||
|
||||
private LongArrayTag parseLongArrayTag() throws ParseException {
|
||||
List<Long> longList = new ArrayList<>();
|
||||
while (ptr.currentChar() != ']') {
|
||||
String s = ptr.parseSimpleString();
|
||||
ptr.skipWhitespace();
|
||||
if (NUMBER_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
longList.add(Long.parseLong(s));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("long not in range: \"" + s + "\"");
|
||||
}
|
||||
} else {
|
||||
throw ptr.parseException("invalid long in LongArrayTag: \"" + s + "\"");
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
return new LongArrayTag(longList.stream().mapToLong(l -> l).toArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.StringSerializer;
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public class SNBTSerializer implements StringSerializer<Tag<?>> {
|
||||
|
||||
@Override
|
||||
public void toWriter(Tag<?> tag, Writer writer) throws IOException {
|
||||
SNBTWriter.write(tag, writer);
|
||||
}
|
||||
|
||||
public void toWriter(Tag<?> tag, Writer writer, int maxDepth) throws IOException {
|
||||
SNBTWriter.write(tag, writer, maxDepth);
|
||||
}
|
||||
}
|
||||
@@ -16,30 +16,19 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
/**
|
||||
* The <code>TAG_End</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class EndTag extends Tag {
|
||||
import com.volmit.iris.util.nbt.tag.Tag;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*/
|
||||
public EndTag() {
|
||||
super("");
|
||||
import java.io.IOException;
|
||||
|
||||
public class SNBTUtil {
|
||||
|
||||
public static String toSNBT(Tag<?> tag) throws IOException {
|
||||
return new SNBTSerializer().toString(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
public static Tag<?> fromSNBT(String string) throws IOException {
|
||||
return new SNBTDeserializer().fromString(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TAG_End";
|
||||
}
|
||||
|
||||
}
|
||||
135
src/main/java/com/volmit/iris/util/nbt/io/SNBTWriter.java
Normal file
135
src/main/java/com/volmit/iris/util/nbt/io/SNBTWriter.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.nbt.tag.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* SNBTWriter creates an SNBT String.
|
||||
*/
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
public final class SNBTWriter implements MaxDepthIO {
|
||||
|
||||
private static final Pattern NON_QUOTE_PATTERN = Pattern.compile("[a-zA-Z_.+\\-]+");
|
||||
|
||||
private final Writer writer;
|
||||
|
||||
private SNBTWriter(Writer writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, Writer writer, int maxDepth) throws IOException {
|
||||
new SNBTWriter(writer).writeAnything(tag, maxDepth);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, Writer writer) throws IOException {
|
||||
write(tag, writer, Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
private void writeAnything(Tag<?> tag, int maxDepth) throws IOException {
|
||||
switch (tag.getID()) {
|
||||
case EndTag.ID:
|
||||
//do nothing
|
||||
break;
|
||||
case ByteTag.ID:
|
||||
writer.append(Byte.toString(((ByteTag) tag).asByte())).write('b');
|
||||
break;
|
||||
case ShortTag.ID:
|
||||
writer.append(Short.toString(((ShortTag) tag).asShort())).write('s');
|
||||
break;
|
||||
case IntTag.ID:
|
||||
writer.write(Integer.toString(((IntTag) tag).asInt()));
|
||||
break;
|
||||
case LongTag.ID:
|
||||
writer.append(Long.toString(((LongTag) tag).asLong())).write('l');
|
||||
break;
|
||||
case FloatTag.ID:
|
||||
writer.append(Float.toString(((FloatTag) tag).asFloat())).write('f');
|
||||
break;
|
||||
case DoubleTag.ID:
|
||||
writer.append(Double.toString(((DoubleTag) tag).asDouble())).write('d');
|
||||
break;
|
||||
case ByteArrayTag.ID:
|
||||
writeArray(((ByteArrayTag) tag).getValue(), ((ByteArrayTag) tag).length(), "B");
|
||||
break;
|
||||
case StringTag.ID:
|
||||
writer.write(escapeString(((StringTag) tag).getValue()));
|
||||
break;
|
||||
case ListTag.ID:
|
||||
writer.write('[');
|
||||
for (int i = 0; i < ((ListTag<?>) tag).size(); i++) {
|
||||
writer.write(i == 0 ? "" : ",");
|
||||
writeAnything(((ListTag<?>) tag).get(i), decrementMaxDepth(maxDepth));
|
||||
}
|
||||
writer.write(']');
|
||||
break;
|
||||
case CompoundTag.ID:
|
||||
writer.write('{');
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Tag<?>> entry : (CompoundTag) tag) {
|
||||
writer.write(first ? "" : ",");
|
||||
writer.append(escapeString(entry.getKey())).write(':');
|
||||
writeAnything(entry.getValue(), decrementMaxDepth(maxDepth));
|
||||
first = false;
|
||||
}
|
||||
writer.write('}');
|
||||
break;
|
||||
case IntArrayTag.ID:
|
||||
writeArray(((IntArrayTag) tag).getValue(), ((IntArrayTag) tag).length(), "I");
|
||||
break;
|
||||
case LongArrayTag.ID:
|
||||
writeArray(((LongArrayTag) tag).getValue(), ((LongArrayTag) tag).length(), "L");
|
||||
break;
|
||||
default:
|
||||
throw new IOException("unknown tag with id \"" + tag.getID() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeArray(Object array, int length, String prefix) throws IOException {
|
||||
writer.append('[').append(prefix).write(';');
|
||||
for (int i = 0; i < length; i++) {
|
||||
writer.append(i == 0 ? "" : ",").write(Array.get(array, i).toString());
|
||||
}
|
||||
writer.write(']');
|
||||
}
|
||||
|
||||
public static String escapeString(String s) {
|
||||
if (!NON_QUOTE_PATTERN.matcher(s).matches()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('"');
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '\\' || c == '"') {
|
||||
sb.append('\\');
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
sb.append('"');
|
||||
return sb.toString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
133
src/main/java/com/volmit/iris/util/nbt/io/StringPointer.java
Normal file
133
src/main/java/com/volmit/iris/util/nbt/io/StringPointer.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.io;
|
||||
|
||||
public class StringPointer {
|
||||
|
||||
private final String value;
|
||||
private int index;
|
||||
|
||||
public StringPointer(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String parseSimpleString() {
|
||||
int oldIndex = index;
|
||||
while (hasNext() && isSimpleChar(currentChar())) {
|
||||
index++;
|
||||
}
|
||||
return value.substring(oldIndex, index);
|
||||
}
|
||||
|
||||
public String parseQuotedString() throws ParseException {
|
||||
int oldIndex = ++index; //ignore beginning quotes
|
||||
StringBuilder sb = null;
|
||||
boolean escape = false;
|
||||
while (hasNext()) {
|
||||
char c = next();
|
||||
if (escape) {
|
||||
if (c != '\\' && c != '"') {
|
||||
throw parseException("invalid escape of '" + c + "'");
|
||||
}
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == '\\') { //escape
|
||||
escape = true;
|
||||
if (sb != null) {
|
||||
continue;
|
||||
}
|
||||
sb = new StringBuilder(value.substring(oldIndex, index - 1));
|
||||
continue;
|
||||
}
|
||||
if (c == '"') {
|
||||
return sb == null ? value.substring(oldIndex, index - 1) : sb.toString();
|
||||
}
|
||||
}
|
||||
if (sb != null) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
throw parseException("missing end quote");
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean nextArrayElement() {
|
||||
skipWhitespace();
|
||||
if (hasNext() && currentChar() == ',') {
|
||||
index++;
|
||||
skipWhitespace();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void expectChar(char c) throws ParseException {
|
||||
skipWhitespace();
|
||||
boolean hasNext = hasNext();
|
||||
if (hasNext && currentChar() == c) {
|
||||
index++;
|
||||
return;
|
||||
}
|
||||
throw parseException("expected '" + c + "' but got " + (hasNext ? "'" + currentChar() + "'" : "EOF"));
|
||||
}
|
||||
|
||||
public void skipWhitespace() {
|
||||
while (hasNext() && Character.isWhitespace(currentChar())) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index < value.length();
|
||||
}
|
||||
|
||||
public boolean hasCharsLeft(int num) {
|
||||
return this.index + num < value.length();
|
||||
}
|
||||
|
||||
public char currentChar() {
|
||||
return value.charAt(index);
|
||||
}
|
||||
|
||||
public char next() {
|
||||
return value.charAt(index++);
|
||||
}
|
||||
|
||||
public void skip(int offset) {
|
||||
index += offset;
|
||||
}
|
||||
|
||||
public char lookAhead(int offset) {
|
||||
return value.charAt(index + offset);
|
||||
}
|
||||
|
||||
private static boolean isSimpleChar(char c) {
|
||||
return c >= 'a' && c <= 'z'
|
||||
|| c >= 'A' && c <= 'Z'
|
||||
|| c >= '0' && c <= '9'
|
||||
|| c == '-'
|
||||
|| c == '+'
|
||||
|| c == '.'
|
||||
|| c == '_';
|
||||
}
|
||||
|
||||
public ParseException parseException(String msg) {
|
||||
return new ParseException(msg, value, index);
|
||||
}
|
||||
}
|
||||
701
src/main/java/com/volmit/iris/util/nbt/mca/Chunk.java
Normal file
701
src/main/java/com/volmit/iris/util/nbt/mca/Chunk.java
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
import com.volmit.iris.util.nbt.io.NBTDeserializer;
|
||||
import com.volmit.iris.util.nbt.io.NBTSerializer;
|
||||
import com.volmit.iris.util.nbt.io.NamedTag;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.nbt.tag.ListTag;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
import static com.volmit.iris.util.nbt.mca.LoadFlags.*;
|
||||
|
||||
public class Chunk {
|
||||
|
||||
public static final int DEFAULT_DATA_VERSION = 1628;
|
||||
|
||||
private boolean partial;
|
||||
|
||||
private int lastMCAUpdate;
|
||||
|
||||
private CompoundTag data;
|
||||
|
||||
private int dataVersion;
|
||||
private long lastUpdate;
|
||||
private long inhabitedTime;
|
||||
private int[] biomes;
|
||||
private CompoundTag heightMaps;
|
||||
private CompoundTag carvingMasks;
|
||||
private final AtomicReferenceArray<Section> sections = new AtomicReferenceArray<>(16);
|
||||
private ListTag<CompoundTag> entities;
|
||||
private ListTag<CompoundTag> tileEntities;
|
||||
private ListTag<CompoundTag> tileTicks;
|
||||
private ListTag<CompoundTag> liquidTicks;
|
||||
private ListTag<ListTag<?>> lights;
|
||||
private ListTag<ListTag<?>> liquidsToBeTicked;
|
||||
private ListTag<ListTag<?>> toBeTicked;
|
||||
private ListTag<ListTag<?>> postProcessing;
|
||||
private String status;
|
||||
private CompoundTag structures;
|
||||
|
||||
Chunk(int lastMCAUpdate) {
|
||||
this.lastMCAUpdate = lastMCAUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new chunk based on raw base data from a region file.
|
||||
*
|
||||
* @param data The raw base data to be used.
|
||||
*/
|
||||
public Chunk(CompoundTag data) {
|
||||
this.data = data;
|
||||
initReferences(ALL_DATA);
|
||||
}
|
||||
|
||||
private void initReferences(long loadFlags) {
|
||||
if (data == null) {
|
||||
throw new NullPointerException("data cannot be null");
|
||||
}
|
||||
CompoundTag level;
|
||||
if ((level = data.getCompoundTag("Level")) == null) {
|
||||
throw new IllegalArgumentException("data does not contain \"Level\" tag");
|
||||
}
|
||||
dataVersion = data.getInt("DataVersion");
|
||||
inhabitedTime = level.getLong("InhabitedTime");
|
||||
lastUpdate = level.getLong("LastUpdate");
|
||||
if ((loadFlags & BIOMES) != 0) {
|
||||
biomes = level.getIntArray("Biomes");
|
||||
}
|
||||
if ((loadFlags & HEIGHTMAPS) != 0) {
|
||||
heightMaps = level.getCompoundTag("Heightmaps");
|
||||
}
|
||||
if ((loadFlags & CARVING_MASKS) != 0) {
|
||||
carvingMasks = level.getCompoundTag("CarvingMasks");
|
||||
}
|
||||
if ((loadFlags & ENTITIES) != 0) {
|
||||
entities = level.containsKey("Entities") ? level.getListTag("Entities").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & TILE_ENTITIES) != 0) {
|
||||
tileEntities = level.containsKey("TileEntities") ? level.getListTag("TileEntities").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & TILE_TICKS) != 0) {
|
||||
tileTicks = level.containsKey("TileTicks") ? level.getListTag("TileTicks").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & LIQUID_TICKS) != 0) {
|
||||
liquidTicks = level.containsKey("LiquidTicks") ? level.getListTag("LiquidTicks").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & LIGHTS) != 0) {
|
||||
lights = level.containsKey("Lights") ? level.getListTag("Lights").asListTagList() : null;
|
||||
}
|
||||
if ((loadFlags & LIQUIDS_TO_BE_TICKED) != 0) {
|
||||
liquidsToBeTicked = level.containsKey("LiquidsToBeTicked") ? level.getListTag("LiquidsToBeTicked").asListTagList() : null;
|
||||
}
|
||||
if ((loadFlags & TO_BE_TICKED) != 0) {
|
||||
toBeTicked = level.containsKey("ToBeTicked") ? level.getListTag("ToBeTicked").asListTagList() : null;
|
||||
}
|
||||
if ((loadFlags & POST_PROCESSING) != 0) {
|
||||
postProcessing = level.containsKey("PostProcessing") ? level.getListTag("PostProcessing").asListTagList() : null;
|
||||
}
|
||||
status = level.getString("Status");
|
||||
if ((loadFlags & STRUCTURES) != 0) {
|
||||
structures = level.getCompoundTag("Structures");
|
||||
}
|
||||
if ((loadFlags & (BLOCK_LIGHTS | BLOCK_STATES | SKY_LIGHT)) != 0 && level.containsKey("Sections")) {
|
||||
for (CompoundTag section : level.getListTag("Sections").asCompoundTagList()) {
|
||||
int sectionIndex = section.getByte("Y");
|
||||
if (sectionIndex > 15 || sectionIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
Section newSection = new Section(section, dataVersion, loadFlags);
|
||||
if (newSection.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
sections.set(sectionIndex, newSection);
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't requested the full set of data we can drop the underlying raw data to let the GC handle it.
|
||||
if (loadFlags != ALL_DATA) {
|
||||
data = null;
|
||||
partial = true;
|
||||
} else {
|
||||
partial = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this chunk to a <code>RandomAccessFile</code>.
|
||||
*
|
||||
* @param raf The RandomAccessFile to be written to.
|
||||
* @param xPos The x-coordinate of the chunk.
|
||||
* @param zPos The z-coodrinate of the chunk.
|
||||
* @return The amount of bytes written to the RandomAccessFile.
|
||||
* @throws UnsupportedOperationException When something went wrong during writing.
|
||||
* @throws IOException When something went wrong during writing.
|
||||
*/
|
||||
public int serialize(RandomAccessFile raf, int xPos, int zPos) throws IOException {
|
||||
if (partial) {
|
||||
throw new UnsupportedOperationException("Partially loaded chunks cannot be serialized");
|
||||
}
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
|
||||
try (BufferedOutputStream nbtOut = new BufferedOutputStream(CompressionType.ZLIB.compress(baos))) {
|
||||
new NBTSerializer(false).toStream(new NamedTag(null, updateHandle(xPos, zPos)), nbtOut);
|
||||
}
|
||||
byte[] rawData = baos.toByteArray();
|
||||
raf.writeInt(rawData.length + 1); // including the byte to store the compression type
|
||||
raf.writeByte(CompressionType.ZLIB.getID());
|
||||
raf.write(rawData);
|
||||
return rawData.length + 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads chunk data from a RandomAccessFile. The RandomAccessFile must already be at the correct position.
|
||||
*
|
||||
* @param raf The RandomAccessFile to read the chunk data from.
|
||||
* @throws IOException When something went wrong during reading.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf) throws IOException {
|
||||
deserialize(raf, ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads chunk data from a RandomAccessFile. The RandomAccessFile must already be at the correct position.
|
||||
*
|
||||
* @param raf The RandomAccessFile to read the chunk data from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @throws IOException When something went wrong during reading.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf, long loadFlags) throws IOException {
|
||||
byte compressionTypeByte = raf.readByte();
|
||||
CompressionType compressionType = CompressionType.getFromID(compressionTypeByte);
|
||||
if (compressionType == null) {
|
||||
throw new IOException("invalid compression type " + compressionTypeByte);
|
||||
}
|
||||
BufferedInputStream dis = new BufferedInputStream(compressionType.decompress(new FileInputStream(raf.getFD())));
|
||||
NamedTag tag = new NBTDeserializer(false).fromStream(dis);
|
||||
if (tag != null && tag.getTag() instanceof CompoundTag) {
|
||||
data = (CompoundTag) tag.getTag();
|
||||
initReferences(loadFlags);
|
||||
} else {
|
||||
throw new IOException("invalid data tag: " + (tag == null ? "null" : tag.getClass().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getBiomeAt(int, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public int getBiomeAt(int blockX, int blockZ) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
return -1;
|
||||
}
|
||||
return biomes[getBlockIndex(blockX, blockZ)];
|
||||
} else {
|
||||
throw new IllegalStateException("cannot get biome using Chunk#getBiomeAt(int,int) from biome data with DataVersion of 2202 or higher, use Chunk#getBiomeAt(int,int,int) instead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a biome id at a specific block in this chunk.
|
||||
* The coordinates can be absolute coordinates or relative to the region or chunk.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @return The biome id or -1 if the biomes are not correctly initialized.
|
||||
*/
|
||||
public int getBiomeAt(int blockX, int blockY, int blockZ) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
return -1;
|
||||
}
|
||||
return biomes[getBlockIndex(blockX, blockZ)];
|
||||
} else {
|
||||
if (biomes == null || biomes.length != 1024) {
|
||||
return -1;
|
||||
}
|
||||
int biomeX = (blockX & 0xF) >> 2;
|
||||
int biomeY = (blockY & 0xF) >> 2;
|
||||
int biomeZ = (blockZ & 0xF) >> 2;
|
||||
|
||||
return biomes[getBiomeIndex(biomeX, biomeY, biomeZ)];
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setBiomeAt(int blockX, int blockZ, int biomeID) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
biomes = new int[256];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
biomes[getBlockIndex(blockX, blockZ)] = biomeID;
|
||||
} else {
|
||||
if (biomes == null || biomes.length != 1024) {
|
||||
biomes = new int[1024];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
|
||||
int biomeX = (blockX & 0xF) >> 2;
|
||||
int biomeZ = (blockZ & 0xF) >> 2;
|
||||
|
||||
for (int y = 0; y < 64; y++) {
|
||||
biomes[getBiomeIndex(biomeX, y, biomeZ)] = biomeID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a biome id at a specific block column.
|
||||
* The coordinates can be absolute coordinates or relative to the region or chunk.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block column.
|
||||
* @param blockZ The z-coordinate of the block column.
|
||||
* @param biomeID The biome id to be set.
|
||||
* When set to a negative number, Minecraft will replace it with the block column's default biome.
|
||||
*/
|
||||
public void setBiomeAt(int blockX, int blockY, int blockZ, int biomeID) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
biomes = new int[256];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
biomes[getBlockIndex(blockX, blockZ)] = biomeID;
|
||||
} else {
|
||||
if (biomes == null || biomes.length != 1024) {
|
||||
biomes = new int[1024];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
|
||||
int biomeX = (blockX & 0xF) >> 2;
|
||||
int biomeZ = (blockZ & 0xF) >> 2;
|
||||
|
||||
biomes[getBiomeIndex(biomeX, blockY, biomeZ)] = biomeID;
|
||||
}
|
||||
}
|
||||
|
||||
int getBiomeIndex(int biomeX, int biomeY, int biomeZ) {
|
||||
return biomeY * 64 + biomeZ * 4 + biomeX;
|
||||
}
|
||||
|
||||
public CompoundTag getBlockStateAt(int blockX, int blockY, int blockZ) {
|
||||
Section section = sections.get(MCAUtil.blockToChunk(blockY));
|
||||
if (section == null) {
|
||||
return null;
|
||||
}
|
||||
return section.getBlockStateAt(blockX, blockY, blockZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a block state at a specific location.
|
||||
* The block coordinates can be absolute or relative to the region or chunk.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @param state The block state to be set.
|
||||
* @param cleanup When <code>true</code>, it will cleanup all palettes of this chunk.
|
||||
* This option should only be used moderately to avoid unnecessary recalculation of the palette indices.
|
||||
* Recalculating the Palette should only be executed once right before saving the Chunk to file.
|
||||
*/
|
||||
public void setBlockStateAt(int blockX, int blockY, int blockZ, CompoundTag state, boolean cleanup) {
|
||||
int sectionIndex = MCAUtil.blockToChunk(blockY);
|
||||
Section section = sections.get(sectionIndex);
|
||||
if (section == null) {
|
||||
section = Section.newSection();
|
||||
sections.set(sectionIndex, section);
|
||||
}
|
||||
section.setBlockStateAt(blockX, blockY, blockZ, state, cleanup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The DataVersion of this chunk.
|
||||
*/
|
||||
public int getDataVersion() {
|
||||
return dataVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DataVersion of this chunk. This does not check if the data of this chunk conforms
|
||||
* to that DataVersion, that is the responsibility of the developer.
|
||||
*
|
||||
* @param dataVersion The DataVersion to be set.
|
||||
*/
|
||||
public void setDataVersion(int dataVersion) {
|
||||
this.dataVersion = dataVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The timestamp when this region file was last updated in seconds since 1970-01-01.
|
||||
*/
|
||||
public int getLastMCAUpdate() {
|
||||
return lastMCAUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timestamp when this region file was last updated in seconds since 1970-01-01.
|
||||
*
|
||||
* @param lastMCAUpdate The time in seconds since 1970-01-01.
|
||||
*/
|
||||
public void setLastMCAUpdate(int lastMCAUpdate) {
|
||||
this.lastMCAUpdate = lastMCAUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The generation station of this chunk.
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the generation status of this chunk.
|
||||
*
|
||||
* @param status The generation status of this chunk.
|
||||
*/
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the section at the given y-coordinate.
|
||||
*
|
||||
* @param sectionY The y-coordinate of the section in this chunk ranging from 0 to 15.
|
||||
* @return The Section.
|
||||
*/
|
||||
public Section getSection(int sectionY) {
|
||||
return sections.get(sectionY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a section at a givesn y-coordinate
|
||||
*
|
||||
* @param sectionY The y-coordinate of the section in this chunk ranging from 0 to 15.
|
||||
* @param section The section to be set.
|
||||
*/
|
||||
public void setSection(int sectionY, Section section) {
|
||||
sections.set(sectionY, section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The timestamp when this chunk was last updated as a UNIX timestamp.
|
||||
*/
|
||||
public long getLastUpdate() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time when this chunk was last updated as a UNIX timestamp.
|
||||
*
|
||||
* @param lastUpdate The UNIX timestamp.
|
||||
*/
|
||||
public void setLastUpdate(long lastUpdate) {
|
||||
this.lastUpdate = lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The cumulative amount of time players have spent in this chunk in ticks.
|
||||
*/
|
||||
public long getInhabitedTime() {
|
||||
return inhabitedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cumulative amount of time players have spent in this chunk in ticks.
|
||||
*
|
||||
* @param inhabitedTime The time in ticks.
|
||||
*/
|
||||
public void setInhabitedTime(long inhabitedTime) {
|
||||
this.inhabitedTime = inhabitedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A matrix of biome IDs for all block columns in this chunk.
|
||||
*/
|
||||
public int[] getBiomes() {
|
||||
return biomes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the biome IDs for this chunk.
|
||||
*
|
||||
* @param biomes The biome ID matrix of this chunk. Must have a length of <code>256</code>.
|
||||
* @throws IllegalArgumentException When the biome matrix does not have a length of <code>256</code>
|
||||
* or is <code>null</code>
|
||||
*/
|
||||
public void setBiomes(int[] biomes) {
|
||||
if (biomes != null) {
|
||||
if (dataVersion < 2202 && biomes.length != 256 || dataVersion >= 2202 && biomes.length != 1024) {
|
||||
throw new IllegalArgumentException("biomes array must have a length of " + (dataVersion < 2202 ? "256" : "1024"));
|
||||
}
|
||||
}
|
||||
this.biomes = biomes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height maps of this chunk.
|
||||
*/
|
||||
public CompoundTag getHeightMaps() {
|
||||
return heightMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the height maps of this chunk.
|
||||
*
|
||||
* @param heightMaps The height maps.
|
||||
*/
|
||||
public void setHeightMaps(CompoundTag heightMaps) {
|
||||
this.heightMaps = heightMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The carving masks of this chunk.
|
||||
*/
|
||||
public CompoundTag getCarvingMasks() {
|
||||
return carvingMasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the carving masks of this chunk.
|
||||
*
|
||||
* @param carvingMasks The carving masks.
|
||||
*/
|
||||
public void setCarvingMasks(CompoundTag carvingMasks) {
|
||||
this.carvingMasks = carvingMasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The entities of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entities of this chunk.
|
||||
*
|
||||
* @param entities The entities.
|
||||
*/
|
||||
public void setEntities(ListTag<CompoundTag> entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tile entities of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getTileEntities() {
|
||||
return tileEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tile entities of this chunk.
|
||||
*
|
||||
* @param tileEntities The tile entities of this chunk.
|
||||
*/
|
||||
public void setTileEntities(ListTag<CompoundTag> tileEntities) {
|
||||
this.tileEntities = tileEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tile ticks of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getTileTicks() {
|
||||
return tileTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tile ticks of this chunk.
|
||||
*
|
||||
* @param tileTicks Thee tile ticks.
|
||||
*/
|
||||
public void setTileTicks(ListTag<CompoundTag> tileTicks) {
|
||||
this.tileTicks = tileTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The liquid ticks of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getLiquidTicks() {
|
||||
return liquidTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the liquid ticks of this chunk.
|
||||
*
|
||||
* @param liquidTicks The liquid ticks.
|
||||
*/
|
||||
public void setLiquidTicks(ListTag<CompoundTag> liquidTicks) {
|
||||
this.liquidTicks = liquidTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The light sources in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getLights() {
|
||||
return lights;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the light sources in this chunk.
|
||||
*
|
||||
* @param lights The light sources.
|
||||
*/
|
||||
public void setLights(ListTag<ListTag<?>> lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return THe liquids to be ticked in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getLiquidsToBeTicked() {
|
||||
return liquidsToBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the liquids to be ticked in this chunk.
|
||||
*
|
||||
* @param liquidsToBeTicked The liquids to be ticked.
|
||||
*/
|
||||
public void setLiquidsToBeTicked(ListTag<ListTag<?>> liquidsToBeTicked) {
|
||||
this.liquidsToBeTicked = liquidsToBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Stuff to be ticked in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getToBeTicked() {
|
||||
return toBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets stuff to be ticked in this chunk.
|
||||
*
|
||||
* @param toBeTicked The stuff to be ticked.
|
||||
*/
|
||||
public void setToBeTicked(ListTag<ListTag<?>> toBeTicked) {
|
||||
this.toBeTicked = toBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Things that are in post processing in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getPostProcessing() {
|
||||
return postProcessing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets things to be post processed in this chunk.
|
||||
*
|
||||
* @param postProcessing The things to be post processed.
|
||||
*/
|
||||
public void setPostProcessing(ListTag<ListTag<?>> postProcessing) {
|
||||
this.postProcessing = postProcessing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Data about structures in this chunk.
|
||||
*/
|
||||
public CompoundTag getStructures() {
|
||||
return structures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data about structures in this chunk.
|
||||
*
|
||||
* @param structures The data about structures.
|
||||
*/
|
||||
public void setStructures(CompoundTag structures) {
|
||||
this.structures = structures;
|
||||
}
|
||||
|
||||
int getBlockIndex(int blockX, int blockZ) {
|
||||
return (blockZ & 0xF) * 16 + (blockX & 0xF);
|
||||
}
|
||||
|
||||
public void cleanupPalettesAndBlockStates() {
|
||||
for (int i = 0; i < sections.length(); i++) {
|
||||
Section section = sections.get(i);
|
||||
if (section != null) {
|
||||
section.cleanupPaletteAndBlockStates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Chunk newChunk() {
|
||||
Chunk c = new Chunk(0);
|
||||
c.dataVersion = DEFAULT_DATA_VERSION;
|
||||
c.data = new CompoundTag();
|
||||
c.data.put("Level", new CompoundTag());
|
||||
c.status = "mobs_spawned";
|
||||
return c;
|
||||
}
|
||||
|
||||
public CompoundTag updateHandle(int xPos, int zPos) {
|
||||
data.putInt("DataVersion", dataVersion);
|
||||
CompoundTag level = data.getCompoundTag("Level");
|
||||
level.putInt("xPos", xPos);
|
||||
level.putInt("zPos", zPos);
|
||||
level.putLong("LastUpdate", lastUpdate);
|
||||
level.putLong("InhabitedTime", inhabitedTime);
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes != null && biomes.length == 256) level.putIntArray("Biomes", biomes);
|
||||
} else {
|
||||
if (biomes != null && biomes.length == 1024) level.putIntArray("Biomes", biomes);
|
||||
}
|
||||
if (heightMaps != null) level.put("Heightmaps", heightMaps);
|
||||
if (carvingMasks != null) level.put("CarvingMasks", carvingMasks);
|
||||
if (entities != null) level.put("Entities", entities);
|
||||
if (tileEntities != null) level.put("TileEntities", tileEntities);
|
||||
if (tileTicks != null) level.put("TileTicks", tileTicks);
|
||||
if (liquidTicks != null) level.put("LiquidTicks", liquidTicks);
|
||||
if (lights != null) level.put("Lights", lights);
|
||||
if (liquidsToBeTicked != null) level.put("LiquidsToBeTicked", liquidsToBeTicked);
|
||||
if (toBeTicked != null) level.put("ToBeTicked", toBeTicked);
|
||||
if (postProcessing != null) level.put("PostProcessing", postProcessing);
|
||||
level.putString("Status", status);
|
||||
if (structures != null) level.put("Structures", structures);
|
||||
ListTag<CompoundTag> sections = new ListTag<>(CompoundTag.class);
|
||||
for (int i = 0; i < this.sections.length(); i++) {
|
||||
if (this.sections.get(i) != null) {
|
||||
sections.add(this.sections.get(i).updateHandle(i));
|
||||
}
|
||||
}
|
||||
level.put("Sections", sections);
|
||||
return data;
|
||||
}
|
||||
|
||||
public int sectionCount() {
|
||||
return sections.length();
|
||||
}
|
||||
|
||||
public void runLighting() {
|
||||
for (int s = 15; s >= 0; s--) {
|
||||
Section section = getSection(s);
|
||||
|
||||
if (section != null) {
|
||||
section.runLighting();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public enum CompressionType {
|
||||
|
||||
NONE(0, t -> t, t -> t),
|
||||
GZIP(1, GZIPOutputStream::new, GZIPInputStream::new),
|
||||
ZLIB(2, DeflaterOutputStream::new, InflaterInputStream::new);
|
||||
|
||||
private final byte id;
|
||||
private final ExceptionFunction<OutputStream, ? extends OutputStream, IOException> compressor;
|
||||
private final ExceptionFunction<InputStream, ? extends InputStream, IOException> decompressor;
|
||||
|
||||
CompressionType(int id,
|
||||
ExceptionFunction<OutputStream, ? extends OutputStream, IOException> compressor,
|
||||
ExceptionFunction<InputStream, ? extends InputStream, IOException> decompressor) {
|
||||
this.id = (byte) id;
|
||||
this.compressor = compressor;
|
||||
this.decompressor = decompressor;
|
||||
}
|
||||
|
||||
public byte getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public OutputStream compress(OutputStream out) throws IOException {
|
||||
return compressor.accept(out);
|
||||
}
|
||||
|
||||
public InputStream decompress(InputStream in) throws IOException {
|
||||
return decompressor.accept(in);
|
||||
}
|
||||
|
||||
public static CompressionType getFromID(byte id) {
|
||||
for (CompressionType c : CompressionType.values()) {
|
||||
if (c.id == id) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ExceptionFunction<T, R, E extends Exception> {
|
||||
|
||||
R accept(T t) throws E;
|
||||
}
|
||||
42
src/main/java/com/volmit/iris/util/nbt/mca/LoadFlags.java
Normal file
42
src/main/java/com/volmit/iris/util/nbt/mca/LoadFlags.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
public class LoadFlags {
|
||||
|
||||
public static final long BIOMES = 0x0001;
|
||||
public static final long HEIGHTMAPS = 0x0002;
|
||||
public static final long CARVING_MASKS = 0x0004;
|
||||
public static final long ENTITIES = 0x0008;
|
||||
public static final long TILE_ENTITIES = 0x0010;
|
||||
public static final long TILE_TICKS = 0x0040;
|
||||
public static final long LIQUID_TICKS = 0x0080;
|
||||
public static final long TO_BE_TICKED = 0x0100;
|
||||
public static final long POST_PROCESSING = 0x0200;
|
||||
public static final long STRUCTURES = 0x0400;
|
||||
public static final long BLOCK_LIGHTS = 0x0800;
|
||||
public static final long BLOCK_STATES = 0x1000;
|
||||
public static final long SKY_LIGHT = 0x2000;
|
||||
public static final long LIGHTS = 0x4000;
|
||||
public static final long LIQUIDS_TO_BE_TICKED = 0x8000;
|
||||
|
||||
public static final long ALL_DATA = 0xffffffffffffffffL;
|
||||
|
||||
|
||||
}
|
||||
350
src/main/java/com/volmit/iris/util/nbt/mca/MCAFile.java
Normal file
350
src/main/java/com/volmit/iris/util/nbt/mca/MCAFile.java
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class MCAFile {
|
||||
|
||||
/**
|
||||
* The default chunk data version used when no custom version is supplied.
|
||||
*/
|
||||
public static final int DEFAULT_DATA_VERSION = 1628;
|
||||
|
||||
private final int regionX;
|
||||
private final int regionZ;
|
||||
private AtomicReferenceArray<Chunk> chunks;
|
||||
|
||||
/**
|
||||
* MCAFile represents a world save file used by Minecraft to store world
|
||||
* data on the hard drive.
|
||||
* This constructor needs the x- and z-coordinates of the stored region,
|
||||
* which can usually be taken from the file name {@code r.x.z.mca}
|
||||
*
|
||||
* @param regionX The x-coordinate of this region.
|
||||
* @param regionZ The z-coordinate of this region.
|
||||
*/
|
||||
public MCAFile(int regionX, int regionZ) {
|
||||
this.regionX = regionX;
|
||||
this.regionZ = regionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an .mca file from a {@code RandomAccessFile} into this object.
|
||||
* This method does not perform any cleanups on the data.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to read from.
|
||||
* @throws IOException If something went wrong during deserialization.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf) throws IOException {
|
||||
deserialize(raf, LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an .mca file from a {@code RandomAccessFile} into this object.
|
||||
* This method does not perform any cleanups on the data.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to read from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @throws IOException If something went wrong during deserialization.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf, long loadFlags) throws IOException {
|
||||
chunks = new AtomicReferenceArray<>(1024);
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
raf.seek(i * 4);
|
||||
int offset = raf.read() << 16;
|
||||
offset |= (raf.read() & 0xFF) << 8;
|
||||
offset |= raf.read() & 0xFF;
|
||||
if (raf.readByte() == 0) {
|
||||
continue;
|
||||
}
|
||||
raf.seek(4096 + i * 4);
|
||||
int timestamp = raf.readInt();
|
||||
Chunk chunk = new Chunk(timestamp);
|
||||
raf.seek(4096L * offset + 4); //+4: skip data size
|
||||
chunk.deserialize(raf, loadFlags);
|
||||
chunks.set(i, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public KList<Position2> samplePositions(RandomAccessFile raf) throws IOException {
|
||||
KList<Position2> p2 = new KList<>();
|
||||
chunks = new AtomicReferenceArray<>(1024);
|
||||
int x = 0;
|
||||
int z = 0;
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
x++;
|
||||
z++;
|
||||
|
||||
raf.seek(i * 4);
|
||||
int offset = raf.read() << 16;
|
||||
offset |= (raf.read() & 0xFF) << 8;
|
||||
offset |= raf.read() & 0xFF;
|
||||
if (raf.readByte() == 0) {
|
||||
continue;
|
||||
}
|
||||
p2.add(new Position2(x & 31, (z / 32) & 31));
|
||||
}
|
||||
return p2;
|
||||
}
|
||||
|
||||
public AtomicReferenceArray<Chunk> getChunks() {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MCAFile#serialize(RandomAccessFile, boolean)} without updating any timestamps.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to write to.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something went wrong during serialization.
|
||||
* @see MCAFile#serialize(RandomAccessFile, boolean)
|
||||
*/
|
||||
public int serialize(RandomAccessFile raf) throws IOException {
|
||||
return serialize(raf, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this object to an .mca file.
|
||||
* This method does not perform any cleanups on the data.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to write to.
|
||||
* @param changeLastUpdate Whether it should update all timestamps that show
|
||||
* when this file was last updated.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something went wrong during serialization.
|
||||
*/
|
||||
public int serialize(RandomAccessFile raf, boolean changeLastUpdate) throws IOException {
|
||||
int globalOffset = 2;
|
||||
int lastWritten = 0;
|
||||
int timestamp = (int) (System.currentTimeMillis() / 1000L);
|
||||
int chunksWritten = 0;
|
||||
int chunkXOffset = MCAUtil.regionToChunk(regionX);
|
||||
int chunkZOffset = MCAUtil.regionToChunk(regionZ);
|
||||
|
||||
if (chunks == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int cx = 0; cx < 32; cx++) {
|
||||
for (int cz = 0; cz < 32; cz++) {
|
||||
int index = getChunkIndex(cx, cz);
|
||||
Chunk chunk = chunks.get(index);
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
raf.seek(4096L * globalOffset);
|
||||
lastWritten = chunk.serialize(raf, chunkXOffset + cx, chunkZOffset + cz);
|
||||
|
||||
if (lastWritten == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
chunksWritten++;
|
||||
|
||||
int sectors = (lastWritten >> 12) + (lastWritten % 4096 == 0 ? 0 : 1);
|
||||
|
||||
raf.seek(index * 4L);
|
||||
raf.writeByte(globalOffset >>> 16);
|
||||
raf.writeByte(globalOffset >> 8 & 0xFF);
|
||||
raf.writeByte(globalOffset & 0xFF);
|
||||
raf.writeByte(sectors);
|
||||
|
||||
// write timestamp
|
||||
raf.seek(index * 4L + 4096);
|
||||
raf.writeInt(changeLastUpdate ? timestamp : chunk.getLastMCAUpdate());
|
||||
|
||||
globalOffset += sectors;
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
if (lastWritten % 4096 != 0) {
|
||||
raf.seek(globalOffset * 4096L - 1);
|
||||
raf.write(0);
|
||||
}
|
||||
return chunksWritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific Chunk at a specific index. The index must be in range of 0 - 1023.
|
||||
*
|
||||
* @param index The index of the Chunk.
|
||||
* @param chunk The Chunk to be set.
|
||||
* @throws IndexOutOfBoundsException If index is not in the range.
|
||||
*/
|
||||
public void setChunk(int index, Chunk chunk) {
|
||||
checkIndex(index);
|
||||
if (chunks == null) {
|
||||
chunks = new AtomicReferenceArray<>(1024);
|
||||
}
|
||||
chunks.set(index, chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific Chunk at a specific chunk location.
|
||||
* The x- and z-value can be absolute chunk coordinates or they can be relative to the region origin.
|
||||
*
|
||||
* @param chunkX The x-coordinate of the Chunk.
|
||||
* @param chunkZ The z-coordinate of the Chunk.
|
||||
* @param chunk The chunk to be set.
|
||||
*/
|
||||
public void setChunk(int chunkX, int chunkZ, Chunk chunk) {
|
||||
setChunk(getChunkIndex(chunkX, chunkZ), chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chunk data of a chunk at a specific index in this file.
|
||||
*
|
||||
* @param index The index of the chunk in this file.
|
||||
* @return The chunk data.
|
||||
*/
|
||||
public Chunk getChunk(int index) {
|
||||
checkIndex(index);
|
||||
if (chunks == null) {
|
||||
return null;
|
||||
}
|
||||
return chunks.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chunk data of a chunk in this file.
|
||||
*
|
||||
* @param chunkX The x-coordinate of the chunk.
|
||||
* @param chunkZ The z-coordinate of the chunk.
|
||||
* @return The chunk data.
|
||||
*/
|
||||
public Chunk getChunk(int chunkX, int chunkZ) {
|
||||
return getChunk(getChunkIndex(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
public boolean hasChunk(int chunkX, int chunkZ) {
|
||||
return getChunk(chunkX, chunkZ) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the index of a chunk from its x- and z-coordinates in this region.
|
||||
* This works with absolute and relative coordinates.
|
||||
*
|
||||
* @param chunkX The x-coordinate of the chunk.
|
||||
* @param chunkZ The z-coordinate of the chunk.
|
||||
* @return The index of this chunk.
|
||||
*/
|
||||
public static int getChunkIndex(int chunkX, int chunkZ) {
|
||||
return (chunkX & 0x1F) + (chunkZ & 0x1F) * 32;
|
||||
}
|
||||
|
||||
private int checkIndex(int index) {
|
||||
if (index < 0 || index > 1023) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private Chunk createChunkIfMissing(int blockX, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(chunkX, chunkZ);
|
||||
if (chunk == null) {
|
||||
chunk = Chunk.newChunk();
|
||||
setChunk(getChunkIndex(chunkX, chunkZ), chunk);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setBiomeAt(int, int, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setBiomeAt(int blockX, int blockZ, int biomeID) {
|
||||
createChunkIfMissing(blockX, blockZ).setBiomeAt(blockX, blockZ, biomeID);
|
||||
}
|
||||
|
||||
public void setBiomeAt(int blockX, int blockY, int blockZ, int biomeID) {
|
||||
createChunkIfMissing(blockX, blockZ).setBiomeAt(blockX, blockY, blockZ, biomeID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getBiomeAt(int, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public int getBiomeAt(int blockX, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(getChunkIndex(chunkX, chunkZ));
|
||||
if (chunk == null) {
|
||||
return -1;
|
||||
}
|
||||
return chunk.getBiomeAt(blockX, blockZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the biome id at a specific block.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @return The biome id if the chunk exists and the chunk has biomes, otherwise -1.
|
||||
*/
|
||||
public int getBiomeAt(int blockX, int blockY, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(getChunkIndex(chunkX, chunkZ));
|
||||
if (chunk == null) {
|
||||
return -1;
|
||||
}
|
||||
return chunk.getBiomeAt(blockX, blockY, blockZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a block state at a specific block location.
|
||||
* The block coordinates can be absolute coordinates or they can be relative to the region.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @param state The block state to be set.
|
||||
* @param cleanup Whether the Palette and the BLockStates should be recalculated after adding the block state.
|
||||
*/
|
||||
public void setBlockStateAt(int blockX, int blockY, int blockZ, CompoundTag state, boolean cleanup) {
|
||||
createChunkIfMissing(blockX, blockZ).setBlockStateAt(blockX, blockY, blockZ, state, cleanup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a block state at a specific block location.
|
||||
* The block coordinates can be absolute coordinates or they can be relative to the region.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @return The block state or <code>null</code> if the chunk or the section do not exist.
|
||||
*/
|
||||
public CompoundTag getBlockStateAt(int blockX, int blockY, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(chunkX, chunkZ);
|
||||
if (chunk == null) {
|
||||
return null;
|
||||
}
|
||||
return chunk.getBlockStateAt(blockX, blockY, blockZ);
|
||||
}
|
||||
}
|
||||
271
src/main/java/com/volmit/iris/util/nbt/mca/MCAUtil.java
Normal file
271
src/main/java/com/volmit/iris/util/nbt/mca/MCAUtil.java
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Provides main and utility functions to read and write .mca files and
|
||||
* to convert block, chunk and region coordinates.
|
||||
*/
|
||||
public final class MCAUtil {
|
||||
|
||||
private MCAUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file The file to read the data from.
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data.
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
* @see MCAUtil#read(File)
|
||||
*/
|
||||
public static MCAFile read(String file) throws IOException {
|
||||
return read(new File(file), LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an MCA file and loads all of its chunks.
|
||||
*
|
||||
* @param file The file to read the data from.
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data.
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
*/
|
||||
public static MCAFile read(File file) throws IOException {
|
||||
return read(file, LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file The file to read the data from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data.
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
* @see MCAUtil#read(File)
|
||||
*/
|
||||
public static MCAFile read(String file, long loadFlags) throws IOException {
|
||||
return read(new File(file), loadFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an MCA file and loads all of its chunks.
|
||||
*
|
||||
* @param file The file to read the data from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
*/
|
||||
public static MCAFile read(File file, long loadFlags) throws IOException {
|
||||
MCAFile mcaFile = newMCAFile(file);
|
||||
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
|
||||
mcaFile.deserialize(raf, loadFlags);
|
||||
return mcaFile;
|
||||
}
|
||||
}
|
||||
|
||||
public static KList<Position2> sampleChunkPositions(File file) throws IOException {
|
||||
MCAFile mcaFile = newMCAFile(file);
|
||||
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
|
||||
return mcaFile.samplePositions(raf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MCAUtil#write(MCAFile, File, boolean)} without changing the timestamps.
|
||||
*
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
* @see MCAUtil#write(MCAFile, File, boolean)
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, String file) throws IOException {
|
||||
return write(mcaFile, new File(file), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MCAUtil#write(MCAFile, File, boolean)} without changing the timestamps.
|
||||
*
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
* @see MCAUtil#write(MCAFile, File, boolean)
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, File file) throws IOException {
|
||||
return write(mcaFile, file, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @param changeLastUpdate Whether to adjust the timestamps of when the file was saved.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
* @see MCAUtil#write(MCAFile, File, boolean)
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, String file, boolean changeLastUpdate) throws IOException {
|
||||
return write(mcaFile, new File(file), changeLastUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an {@code MCAFile} object to disk. It optionally adjusts the timestamps
|
||||
* when the file was last saved to the current date and time or leaves them at
|
||||
* the value set by either loading an already existing MCA file or setting them manually.<br>
|
||||
* If the file already exists, it is completely overwritten by the new file (no modification).
|
||||
*
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @param changeLastUpdate Whether to adjust the timestamps of when the file was saved.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, File file, boolean changeLastUpdate) throws IOException {
|
||||
if (mcaFile == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
File to = file;
|
||||
if (file.exists()) {
|
||||
to = File.createTempFile(to.getName(), null);
|
||||
}
|
||||
int chunks;
|
||||
try (RandomAccessFile raf = new RandomAccessFile(to, "rw")) {
|
||||
chunks = mcaFile.serialize(raf, changeLastUpdate);
|
||||
}
|
||||
|
||||
if (chunks > 0 && to != file) {
|
||||
Files.move(to.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the chunks coordinates into region coordinates and calls
|
||||
* {@link MCAUtil#createNameFromRegionLocation(int, int)}
|
||||
*
|
||||
* @param chunkX The x-value of the location of the chunk.
|
||||
* @param chunkZ The z-value of the location of the chunk.
|
||||
* @return A mca filename in the format "r.{regionX}.{regionZ}.mca"
|
||||
*/
|
||||
public static String createNameFromChunkLocation(int chunkX, int chunkZ) {
|
||||
return createNameFromRegionLocation(chunkToRegion(chunkX), chunkToRegion(chunkZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the block coordinates into region coordinates and calls
|
||||
* {@link MCAUtil#createNameFromRegionLocation(int, int)}
|
||||
*
|
||||
* @param blockX The x-value of the location of the block.
|
||||
* @param blockZ The z-value of the location of the block.
|
||||
* @return A mca filename in the format "r.{regionX}.{regionZ}.mca"
|
||||
*/
|
||||
public static String createNameFromBlockLocation(int blockX, int blockZ) {
|
||||
return createNameFromRegionLocation(blockToRegion(blockX), blockToRegion(blockZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a filename string from provided chunk coordinates.
|
||||
*
|
||||
* @param regionX The x-value of the location of the region.
|
||||
* @param regionZ The z-value of the location of the region.
|
||||
* @return A mca filename in the format "r.{regionX}.{regionZ}.mca"
|
||||
*/
|
||||
public static String createNameFromRegionLocation(int regionX, int regionZ) {
|
||||
return "r." + regionX + "." + regionZ + ".mca";
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a block coordinate value into a chunk coordinate value.
|
||||
*
|
||||
* @param block The block coordinate value.
|
||||
* @return The chunk coordinate value.
|
||||
*/
|
||||
public static int blockToChunk(int block) {
|
||||
return block >> 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a block coordinate value into a region coordinate value.
|
||||
*
|
||||
* @param block The block coordinate value.
|
||||
* @return The region coordinate value.
|
||||
*/
|
||||
public static int blockToRegion(int block) {
|
||||
return block >> 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a chunk coordinate value into a region coordinate value.
|
||||
*
|
||||
* @param chunk The chunk coordinate value.
|
||||
* @return The region coordinate value.
|
||||
*/
|
||||
public static int chunkToRegion(int chunk) {
|
||||
return chunk >> 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a region coordinate value into a chunk coordinate value.
|
||||
*
|
||||
* @param region The region coordinate value.
|
||||
* @return The chunk coordinate value.
|
||||
*/
|
||||
public static int regionToChunk(int region) {
|
||||
return region << 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a region coordinate value into a block coordinate value.
|
||||
*
|
||||
* @param region The region coordinate value.
|
||||
* @return The block coordinate value.
|
||||
*/
|
||||
public static int regionToBlock(int region) {
|
||||
return region << 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a chunk coordinate value into a block coordinate value.
|
||||
*
|
||||
* @param chunk The chunk coordinate value.
|
||||
* @return The block coordinate value.
|
||||
*/
|
||||
public static int chunkToBlock(int chunk) {
|
||||
return chunk << 4;
|
||||
}
|
||||
|
||||
private static final Pattern mcaFilePattern = Pattern.compile("^.*r\\.(?<regionX>-?\\d+)\\.(?<regionZ>-?\\d+)\\.mca$");
|
||||
|
||||
public static MCAFile newMCAFile(File file) {
|
||||
Matcher m = mcaFilePattern.matcher(file.getName());
|
||||
if (m.find()) {
|
||||
return new MCAFile(Integer.parseInt(m.group("regionX")), Integer.parseInt(m.group("regionZ")));
|
||||
}
|
||||
throw new IllegalArgumentException("invalid mca file name: " + file.getName());
|
||||
}
|
||||
}
|
||||
333
src/main/java/com/volmit/iris/util/nbt/mca/NBTWorld.java
Normal file
333
src/main/java/com/volmit/iris/util/nbt/mca/NBTWorld.java
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMS;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.util.data.B;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.nbt.tag.StringTag;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.math.M;
|
||||
import com.volmit.iris.util.scheduling.IrisLock;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class NBTWorld {
|
||||
private static final BlockData AIR = B.get("AIR");
|
||||
private static final Map<String, CompoundTag> blockDataCache = new KMap<>();
|
||||
private static final Map<Biome, Integer> biomeIds = computeBiomeIDs();
|
||||
private final IrisLock regionLock = new IrisLock("Region");
|
||||
private final KMap<Long, MCAFile> loadedRegions;
|
||||
private final KMap<Long, Long> lastUse;
|
||||
private final File worldFolder;
|
||||
private final ExecutorService saveQueue;
|
||||
|
||||
public NBTWorld(File worldFolder) {
|
||||
this.worldFolder = worldFolder;
|
||||
this.loadedRegions = new KMap<>();
|
||||
this.lastUse = new KMap<>();
|
||||
saveQueue = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("Iris MCA Writer");
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
public void close() {
|
||||
regionLock.lock();
|
||||
|
||||
for (Long i : loadedRegions.k()) {
|
||||
queueSaveUnload(Cache.keyX(i), Cache.keyZ(i));
|
||||
}
|
||||
|
||||
regionLock.unlock();
|
||||
saveQueue.shutdown();
|
||||
try {
|
||||
while (!saveQueue.awaitTermination(3, TimeUnit.SECONDS)) {
|
||||
Iris.info("Still Waiting to save MCA Files...");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void flushNow() {
|
||||
regionLock.lock();
|
||||
|
||||
for (Long i : loadedRegions.k()) {
|
||||
doSaveUnload(Cache.keyX(i), Cache.keyZ(i));
|
||||
}
|
||||
|
||||
regionLock.unlock();
|
||||
}
|
||||
|
||||
public void queueSaveUnload(int x, int z) {
|
||||
saveQueue.submit(() -> doSaveUnload(x, z));
|
||||
}
|
||||
|
||||
public void doSaveUnload(int x, int z) {
|
||||
MCAFile f = getMCAOrNull(x, z);
|
||||
if (f != null) {
|
||||
unloadRegion(x, z);
|
||||
}
|
||||
|
||||
saveRegion(x, z, f);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
regionLock.lock();
|
||||
|
||||
boolean saving = true;
|
||||
|
||||
for (Long i : loadedRegions.k()) {
|
||||
int x = Cache.keyX(i);
|
||||
int z = Cache.keyZ(i);
|
||||
|
||||
if (!lastUse.containsKey(i)) {
|
||||
lastUse.put(i, M.ms());
|
||||
}
|
||||
|
||||
if (shouldUnload(x, z)) {
|
||||
queueSaveUnload(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
Iris.debug("Regions: " + C.GOLD + loadedRegions.size() + C.LIGHT_PURPLE);
|
||||
|
||||
regionLock.unlock();
|
||||
}
|
||||
|
||||
public void queueSave() {
|
||||
|
||||
}
|
||||
|
||||
public synchronized void unloadRegion(int x, int z) {
|
||||
long key = Cache.key(x, z);
|
||||
regionLock.lock();
|
||||
loadedRegions.remove(key);
|
||||
lastUse.remove(key);
|
||||
regionLock.unlock();
|
||||
Iris.debug("Unloaded Region " + C.GOLD + x + " " + z);
|
||||
}
|
||||
|
||||
public void saveRegion(int x, int z) {
|
||||
long k = Cache.key(x, z);
|
||||
MCAFile mca = getMCAOrNull(x, z);
|
||||
try {
|
||||
MCAUtil.write(mca, getRegionFile(x, z), true);
|
||||
Iris.debug("Saved Region " + C.GOLD + x + " " + z);
|
||||
} catch (IOException e) {
|
||||
Iris.error("Failed to save region " + getRegionFile(x, z).getPath());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveRegion(int x, int z, MCAFile mca) {
|
||||
try {
|
||||
MCAUtil.write(mca, getRegionFile(x, z), true);
|
||||
Iris.debug("Saved Region " + C.GOLD + x + " " + z);
|
||||
} catch (IOException e) {
|
||||
Iris.error("Failed to save region " + getRegionFile(x, z).getPath());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldUnload(int x, int z) {
|
||||
return getIdleDuration(x, z) > 60000;
|
||||
}
|
||||
|
||||
public File getRegionFile(int x, int z) {
|
||||
return new File(worldFolder, "region/r." + x + "." + z + ".mca");
|
||||
}
|
||||
|
||||
public static BlockData getBlockData(CompoundTag tag) {
|
||||
if (tag == null) {
|
||||
return B.getAir();
|
||||
}
|
||||
|
||||
StringBuilder p = new StringBuilder(tag.getString("Name"));
|
||||
|
||||
if (tag.containsKey("Properties")) {
|
||||
CompoundTag props = tag.getCompoundTag("Properties");
|
||||
p.append('[');
|
||||
|
||||
for (String i : props.keySet()) {
|
||||
p.append(i).append('=').append(props.getString(i)).append(',');
|
||||
}
|
||||
|
||||
p.deleteCharAt(p.length() - 1).append(']');
|
||||
}
|
||||
|
||||
BlockData b = B.getOrNull(p.toString());
|
||||
|
||||
if (b == null) {
|
||||
return B.getAir();
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
public static CompoundTag getCompound(BlockData blockData) {
|
||||
String data = blockData.getAsString(true);
|
||||
|
||||
if (blockDataCache.containsKey(data)) {
|
||||
return blockDataCache.get(data).clone();
|
||||
}
|
||||
|
||||
CompoundTag s = new CompoundTag();
|
||||
NamespacedKey key = blockData.getMaterial().getKey();
|
||||
s.putString("Name", key.getNamespace() + ":" + key.getKey());
|
||||
|
||||
if (data.contains("[")) {
|
||||
String raw = data.split("\\Q[\\E")[1].replaceAll("\\Q]\\E", "");
|
||||
CompoundTag props = new CompoundTag();
|
||||
if (raw.contains(",")) {
|
||||
for (String i : raw.split("\\Q,\\E")) {
|
||||
String[] m = i.split("\\Q=\\E");
|
||||
String k = m[0];
|
||||
String v = m[1];
|
||||
props.put(k, new StringTag(v));
|
||||
}
|
||||
} else {
|
||||
String[] m = raw.split("\\Q=\\E");
|
||||
String k = m[0];
|
||||
String v = m[1];
|
||||
props.put(k, new StringTag(v));
|
||||
}
|
||||
s.put("Properties", props);
|
||||
}
|
||||
|
||||
blockDataCache.put(data, s.clone());
|
||||
return s;
|
||||
}
|
||||
|
||||
public BlockData getBlockData(int x, int y, int z) {
|
||||
try {
|
||||
CompoundTag tag = getChunkSection(x >> 4, y >> 4, z >> 4).getBlockStateAt(x & 15, y & 15, z & 15);
|
||||
|
||||
if (tag == null) {
|
||||
return AIR;
|
||||
}
|
||||
|
||||
return getBlockData(tag);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
|
||||
}
|
||||
return AIR;
|
||||
}
|
||||
|
||||
public void setBlockData(int x, int y, int z, BlockData data) {
|
||||
getChunkSection(x >> 4, y >> 4, z >> 4).setBlockStateAt(x & 15, y & 15, z & 15, getCompound(data), false);
|
||||
}
|
||||
|
||||
public void setBiome(int x, int y, int z, Biome biome) {
|
||||
getChunk(x >> 4, z >> 4).setBiomeAt(x & 15, y, z & 15, biomeIds.get(biome));
|
||||
}
|
||||
|
||||
public Section getChunkSection(int x, int y, int z) {
|
||||
Chunk c = getChunk(x, z);
|
||||
Section s = c.getSection(y);
|
||||
|
||||
if (s == null) {
|
||||
s = Section.newSection();
|
||||
c.setSection(y, s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public synchronized Chunk getChunk(int x, int z) {
|
||||
MCAFile mca = getMCA(x >> 5, z >> 5);
|
||||
Chunk c = mca.getChunk(x & 31, z & 31);
|
||||
|
||||
if (c == null) {
|
||||
c = Chunk.newChunk();
|
||||
mca.setChunk(x & 31, z & 31, c);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public long getIdleDuration(int x, int z) {
|
||||
Long l = lastUse.get(Cache.key(x, z));
|
||||
|
||||
return l == null ? 0 : (M.ms() - l);
|
||||
}
|
||||
|
||||
public MCAFile getMCA(int x, int z) {
|
||||
long key = Cache.key(x, z);
|
||||
|
||||
regionLock.lock();
|
||||
lastUse.put(key, M.ms());
|
||||
MCAFile mcaf = loadedRegions.get(key);
|
||||
regionLock.unlock();
|
||||
|
||||
if (mcaf == null) {
|
||||
mcaf = new MCAFile(x, z);
|
||||
regionLock.lock();
|
||||
loadedRegions.put(key, mcaf);
|
||||
regionLock.unlock();
|
||||
}
|
||||
|
||||
return mcaf;
|
||||
}
|
||||
|
||||
public MCAFile getMCAOrNull(int x, int z) {
|
||||
long key = Cache.key(x, z);
|
||||
MCAFile ff = null;
|
||||
regionLock.lock();
|
||||
|
||||
if (loadedRegions.containsKey(key)) {
|
||||
lastUse.put(key, M.ms());
|
||||
ff = loadedRegions.get(key);
|
||||
}
|
||||
|
||||
regionLock.unlock();
|
||||
return ff;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return loadedRegions.size();
|
||||
}
|
||||
|
||||
private static Map<Biome, Integer> computeBiomeIDs() {
|
||||
Map<Biome, Integer> biomeIds = new KMap<>();
|
||||
|
||||
for (Biome biome : Biome.values()) {
|
||||
if (!biome.name().equals("CUSTOM")) {
|
||||
biomeIds.put(biome, INMS.get().getBiomeId(biome));
|
||||
}
|
||||
}
|
||||
|
||||
return biomeIds;
|
||||
}
|
||||
}
|
||||
454
src/main/java/com/volmit/iris/util/nbt/mca/Section.java
Normal file
454
src/main/java/com/volmit/iris/util/nbt/mca/Section.java
Normal file
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.mca;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.nbt.tag.ByteArrayTag;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.nbt.tag.ListTag;
|
||||
import com.volmit.iris.util.nbt.tag.LongArrayTag;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLongArray;
|
||||
|
||||
public class Section {
|
||||
private CompoundTag data;
|
||||
private Map<String, List<PaletteIndex>> valueIndexedPalette = new KMap<>();
|
||||
private ListTag<CompoundTag> palette;
|
||||
private byte[] blockLight;
|
||||
private AtomicLongArray blockStates;
|
||||
private byte[] skyLight;
|
||||
private int dataVersion;
|
||||
|
||||
public Section(CompoundTag sectionRoot, int dataVersion) {
|
||||
this(sectionRoot, dataVersion, LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
public Section(CompoundTag sectionRoot, int dataVersion, long loadFlags) {
|
||||
data = sectionRoot;
|
||||
this.dataVersion = dataVersion;
|
||||
ListTag<?> rawPalette = sectionRoot.getListTag("Palette");
|
||||
if (rawPalette == null) {
|
||||
return;
|
||||
}
|
||||
palette = rawPalette.asCompoundTagList();
|
||||
for (int i = 0; i < palette.size(); i++) {
|
||||
CompoundTag data = palette.get(i);
|
||||
putValueIndexedPalette(data, i);
|
||||
}
|
||||
|
||||
ByteArrayTag blockLight = sectionRoot.getByteArrayTag("BlockLight");
|
||||
LongArrayTag blockStates = sectionRoot.getLongArrayTag("BlockStates");
|
||||
ByteArrayTag skyLight = sectionRoot.getByteArrayTag("SkyLight");
|
||||
|
||||
if ((loadFlags & LoadFlags.BLOCK_LIGHTS) != 0) {
|
||||
this.blockLight = blockLight != null ? blockLight.getValue() : null;
|
||||
}
|
||||
if ((loadFlags & LoadFlags.BLOCK_STATES) != 0) {
|
||||
this.blockStates = blockStates != null ? new AtomicLongArray(blockStates.getValue()) : null;
|
||||
}
|
||||
if ((loadFlags & LoadFlags.SKY_LIGHT) != 0) {
|
||||
this.skyLight = skyLight != null ? skyLight.getValue() : null;
|
||||
}
|
||||
}
|
||||
|
||||
Section() {
|
||||
}
|
||||
|
||||
void putValueIndexedPalette(CompoundTag data, int index) {
|
||||
PaletteIndex leaf = new PaletteIndex(data, index);
|
||||
String name = data.getString("Name");
|
||||
List<PaletteIndex> leaves = valueIndexedPalette.get(name);
|
||||
if (leaves == null) {
|
||||
leaves = new ArrayList<>(1);
|
||||
leaves.add(leaf);
|
||||
valueIndexedPalette.put(name, leaves);
|
||||
} else {
|
||||
for (PaletteIndex pal : leaves) {
|
||||
if (pal.data.equals(data)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
leaves.add(leaf);
|
||||
}
|
||||
}
|
||||
|
||||
PaletteIndex getValueIndexedPalette(CompoundTag data) {
|
||||
List<PaletteIndex> leaves = valueIndexedPalette.get(data.getString("Name"));
|
||||
if (leaves == null) {
|
||||
return null;
|
||||
}
|
||||
for (PaletteIndex leaf : leaves) {
|
||||
if (leaf.data.equals(data)) {
|
||||
return leaf;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void runLighting() {
|
||||
for (int x = 1; x < 14; x++) {
|
||||
for (int z = 1; z < 14; z++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
private static class PaletteIndex {
|
||||
|
||||
final CompoundTag data;
|
||||
final int index;
|
||||
|
||||
PaletteIndex(CompoundTag data, int index) {
|
||||
this.data = data;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data of this Section is empty.
|
||||
*
|
||||
* @return true if empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return data == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a block state based on a block location from this section.
|
||||
* The coordinates represent the location of the block inside of this Section.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block in this Section
|
||||
* @param blockY The y-coordinate of the block in this Section
|
||||
* @param blockZ The z-coordinate of the block in this Section
|
||||
* @return The block state data of this block.
|
||||
*/
|
||||
public CompoundTag getBlockStateAt(int blockX, int blockY, int blockZ) {
|
||||
try {
|
||||
int index = getBlockIndex(blockX, blockY, blockZ);
|
||||
int paletteIndex = getPaletteIndex(index);
|
||||
return palette.get(paletteIndex);
|
||||
} catch (Throwable ignored) {
|
||||
Iris.reportError(ignored);
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to add a block state for a specific block location in this Section.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block in this Section
|
||||
* @param blockY The y-coordinate of the block in this Section
|
||||
* @param blockZ The z-coordinate of the block in this Section
|
||||
* @param state The block state to be set
|
||||
* @param cleanup When <code>true</code>, it will cleanup the palette of this section.
|
||||
* This option should only be used moderately to avoid unnecessary recalculation of the palette indices.
|
||||
* Recalculating the Palette should only be executed once right before saving the Section to file.
|
||||
*/
|
||||
public void setBlockStateAt(int blockX, int blockY, int blockZ, CompoundTag state, boolean cleanup) {
|
||||
int paletteSizeBefore = palette.size();
|
||||
int paletteIndex = addToPalette(state);
|
||||
//power of 2 --> bits must increase, but only if the palette size changed
|
||||
//otherwise we would attempt to update all blockstates and the entire palette
|
||||
//every time an existing blockstate was added while having 2^x blockstates in the palette
|
||||
if (paletteSizeBefore != palette.size() && (paletteIndex & (paletteIndex - 1)) == 0) {
|
||||
adjustBlockStateBits(null, blockStates);
|
||||
cleanup = true;
|
||||
}
|
||||
|
||||
setPaletteIndex(getBlockIndex(blockX, blockY, blockZ), paletteIndex, blockStates);
|
||||
|
||||
if (cleanup) {
|
||||
cleanupPaletteAndBlockStates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the block data in the palette.
|
||||
*
|
||||
* @param blockStateIndex The index of the block in this section, ranging from 0-4095.
|
||||
* @return The index of the block data in the palette.
|
||||
*/
|
||||
public int getPaletteIndex(int blockStateIndex) {
|
||||
int bits = blockStates.length() >> 6;
|
||||
|
||||
if (dataVersion < 2527) {
|
||||
double blockStatesIndex = blockStateIndex / (4096D / blockStates.length());
|
||||
int longIndex = (int) blockStatesIndex;
|
||||
int startBit = (int) ((blockStatesIndex - Math.floor(blockStatesIndex)) * 64D);
|
||||
if (startBit + bits > 64) {
|
||||
long prev = bitRange(blockStates.get(longIndex), startBit, 64);
|
||||
long next = bitRange(blockStates.get(longIndex + 1), 0, startBit + bits - 64);
|
||||
return (int) ((next << 64 - startBit) + prev);
|
||||
} else {
|
||||
return (int) bitRange(blockStates.get(longIndex), startBit, startBit + bits);
|
||||
}
|
||||
} else {
|
||||
int indicesPerLong = (int) (64D / bits);
|
||||
int blockStatesIndex = blockStateIndex / indicesPerLong;
|
||||
int startBit = (blockStateIndex % indicesPerLong) * bits;
|
||||
return (int) bitRange(blockStates.get(blockStatesIndex), startBit, startBit + bits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the block data in the BlockStates. Does not adjust the size of the BlockStates array.
|
||||
*
|
||||
* @param blockIndex The index of the block in this section, ranging from 0-4095.
|
||||
* @param paletteIndex The block state to be set (index of block data in the palette).
|
||||
* @param blockStates The block states to be updated.
|
||||
*/
|
||||
public void setPaletteIndex(int blockIndex, int paletteIndex, AtomicLongArray blockStates) {
|
||||
int bits = blockStates.length() >> 6;
|
||||
|
||||
if (dataVersion < 2527) {
|
||||
double blockStatesIndex = blockIndex / (4096D / blockStates.length());
|
||||
int longIndex = (int) blockStatesIndex;
|
||||
int startBit = (int) ((blockStatesIndex - Math.floor(longIndex)) * 64D);
|
||||
if (startBit + bits > 64) {
|
||||
blockStates.set(longIndex, updateBits(blockStates.get(longIndex), paletteIndex, startBit, 64));
|
||||
blockStates.set(longIndex + 1, updateBits(blockStates.get(longIndex + 1), paletteIndex, startBit - 64, startBit + bits - 64));
|
||||
} else {
|
||||
blockStates.set(longIndex, updateBits(blockStates.get(longIndex), paletteIndex, startBit, startBit + bits));
|
||||
}
|
||||
} else {
|
||||
int indicesPerLong = (int) (64D / bits);
|
||||
int blockStatesIndex = blockIndex / indicesPerLong;
|
||||
int startBit = (blockIndex % indicesPerLong) * bits;
|
||||
blockStates.set(blockStatesIndex, updateBits(blockStates.get(blockStatesIndex), paletteIndex, startBit, startBit + bits));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the palette of this Section.
|
||||
*
|
||||
* @return The palette of this Section.
|
||||
*/
|
||||
public ListTag<CompoundTag> getPalette() {
|
||||
return palette;
|
||||
}
|
||||
|
||||
int addToPalette(CompoundTag data) {
|
||||
PaletteIndex index;
|
||||
if ((index = getValueIndexedPalette(data)) != null) {
|
||||
return index.index;
|
||||
}
|
||||
palette.add(data);
|
||||
putValueIndexedPalette(data, palette.size() - 1);
|
||||
return palette.size() - 1;
|
||||
}
|
||||
|
||||
int getBlockIndex(int blockX, int blockY, int blockZ) {
|
||||
return (blockY & 0xF) * 256 + (blockZ & 0xF) * 16 + (blockX & 0xF);
|
||||
}
|
||||
|
||||
static long updateBits(long n, long m, int i, int j) {
|
||||
//replace i to j in n with j - i bits of m
|
||||
long mShifted = i > 0 ? (m & ((1L << j - i) - 1)) << i : (m & ((1L << j - i) - 1)) >>> -i;
|
||||
return ((n & ((j > 63 ? 0 : (~0L << j)) | (i < 0 ? 0 : ((1L << i) - 1L)))) | mShifted);
|
||||
}
|
||||
|
||||
static long bitRange(long value, int from, int to) {
|
||||
int waste = 64 - to;
|
||||
return (value << waste) >>> (waste + from);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method recalculates the palette and its indices.
|
||||
* This should only be used moderately to avoid unnecessary recalculation of the palette indices.
|
||||
* Recalculating the Palette should only be executed once right before saving the Section to file.
|
||||
*/
|
||||
public void cleanupPaletteAndBlockStates() {
|
||||
Map<Integer, Integer> oldToNewMapping = cleanupPalette();
|
||||
adjustBlockStateBits(oldToNewMapping, blockStates);
|
||||
}
|
||||
|
||||
private Map<Integer, Integer> cleanupPalette() {
|
||||
//create index - palette mapping
|
||||
Map<Integer, Integer> allIndices = new HashMap<>();
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
int paletteIndex = getPaletteIndex(i);
|
||||
allIndices.put(paletteIndex, paletteIndex);
|
||||
}
|
||||
//delete unused blocks from palette
|
||||
//start at index 1 because we need to keep minecraft:air
|
||||
int index = 1;
|
||||
valueIndexedPalette = new HashMap<>(valueIndexedPalette.size());
|
||||
putValueIndexedPalette(palette.get(0), 0);
|
||||
for (int i = 1; i < palette.size(); i++) {
|
||||
if (!allIndices.containsKey(index)) {
|
||||
palette.remove(i);
|
||||
i--;
|
||||
} else {
|
||||
putValueIndexedPalette(palette.get(i), i);
|
||||
allIndices.put(index, i);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return allIndices;
|
||||
}
|
||||
|
||||
void adjustBlockStateBits(Map<Integer, Integer> oldToNewMapping, AtomicLongArray blockStates) {
|
||||
//increases or decreases the amount of bits used per BlockState
|
||||
//based on the size of the palette. oldToNewMapping can be used to update indices
|
||||
//if the palette had been cleaned up before using MCAFile#cleanupPalette().
|
||||
|
||||
int newBits = 32 - Integer.numberOfLeadingZeros(palette.size() - 1);
|
||||
newBits = Math.max(newBits, 4);
|
||||
|
||||
AtomicLongArray newBlockStates;
|
||||
|
||||
if (dataVersion < 2527) {
|
||||
newBlockStates = newBits == blockStates.length() / 64 ? blockStates : new AtomicLongArray(newBits * 64);
|
||||
} else {
|
||||
int newLength = (int) Math.ceil(4096D / (64D / newBits));
|
||||
newBlockStates = newBits == blockStates.length() / 64 ? blockStates : new AtomicLongArray(newLength);
|
||||
}
|
||||
if (oldToNewMapping != null) {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
setPaletteIndex(i, oldToNewMapping.get(getPaletteIndex(i)), newBlockStates);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
setPaletteIndex(i, getPaletteIndex(i), newBlockStates);
|
||||
}
|
||||
}
|
||||
this.blockStates = newBlockStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The block light array of this Section
|
||||
*/
|
||||
public byte[] getBlockLight() {
|
||||
return blockLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the block light array for this section.
|
||||
*
|
||||
* @param blockLight The block light array
|
||||
* @throws IllegalArgumentException When the length of the array is not 2048
|
||||
*/
|
||||
public void setBlockLight(byte[] blockLight) {
|
||||
if (blockLight != null && blockLight.length != 2048) {
|
||||
throw new IllegalArgumentException("BlockLight array must have a length of 2048");
|
||||
}
|
||||
this.blockLight = blockLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The indices of the block states of this Section.
|
||||
*/
|
||||
public AtomicLongArray getBlockStates() {
|
||||
return blockStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the block state indices to a custom value.
|
||||
*
|
||||
* @param blockStates The block state indices.
|
||||
* @throws NullPointerException If <code>blockStates</code> is <code>null</code>
|
||||
* @throws IllegalArgumentException When <code>blockStates</code>' length is < 256 or > 4096 and is not a multiple of 64
|
||||
*/
|
||||
public void setBlockStates(AtomicLongArray blockStates) {
|
||||
if (blockStates == null) {
|
||||
throw new NullPointerException("BlockStates cannot be null");
|
||||
} else if (blockStates.length() % 64 != 0 || blockStates.length() < 256 || blockStates.length() > 4096) {
|
||||
throw new IllegalArgumentException("BlockStates must have a length > 255 and < 4097 and must be divisible by 64");
|
||||
}
|
||||
this.blockStates = blockStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sky light values of this Section
|
||||
*/
|
||||
public byte[] getSkyLight() {
|
||||
return skyLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sky light values of this section.
|
||||
*
|
||||
* @param skyLight The custom sky light values
|
||||
* @throws IllegalArgumentException If the length of the array is not 2048
|
||||
*/
|
||||
public void setSkyLight(byte[] skyLight) {
|
||||
if (skyLight != null && skyLight.length != 2048) {
|
||||
throw new IllegalArgumentException("SkyLight array must have a length of 2048");
|
||||
}
|
||||
this.skyLight = skyLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Section with base values.
|
||||
*
|
||||
* @return An empty Section
|
||||
*/
|
||||
public static Section newSection() {
|
||||
Section s = new Section();
|
||||
s.blockStates = new AtomicLongArray(256);
|
||||
s.palette = new ListTag<>(CompoundTag.class);
|
||||
CompoundTag air = new CompoundTag();
|
||||
air.putString("Name", "minecraft:air");
|
||||
s.palette.add(air);
|
||||
s.data = new CompoundTag();
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the raw CompoundTag that this Section is based on.
|
||||
* This must be called before saving a Section to disk if the Section was manually created
|
||||
* to set the Y of this Section.
|
||||
*
|
||||
* @param y The Y-value of this Section
|
||||
* @return A reference to the raw CompoundTag this Section is based on
|
||||
*/
|
||||
public CompoundTag updateHandle(int y) {
|
||||
data.putByte("Y", (byte) y);
|
||||
if (palette != null) {
|
||||
data.put("Palette", palette);
|
||||
}
|
||||
if (blockLight != null) {
|
||||
data.putByteArray("BlockLight", blockLight);
|
||||
}
|
||||
if (blockStates != null) {
|
||||
long[] c = new long[blockStates.length()];
|
||||
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
c[i] = blockStates.get(i);
|
||||
}
|
||||
|
||||
data.putLongArray("BlockStates", c);
|
||||
}
|
||||
if (skyLight != null) {
|
||||
data.putByteArray("SkyLight", skyLight);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
65
src/main/java/com/volmit/iris/util/nbt/tag/ArrayTag.java
Normal file
65
src/main/java/com/volmit/iris/util/nbt/tag/ArrayTag.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
/**
|
||||
* ArrayTag is an abstract representation of any NBT array tag.
|
||||
* For implementations see {@link ByteArrayTag}, {@link IntArrayTag}, {@link LongArrayTag}.
|
||||
*
|
||||
* @param <T> The array type.
|
||||
*/
|
||||
public abstract class ArrayTag<T> extends Tag<T> {
|
||||
|
||||
public ArrayTag(T value) {
|
||||
super(value);
|
||||
if (!value.getClass().isArray()) {
|
||||
throw new UnsupportedOperationException("type of array tag must be an array");
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return Array.getLength(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return super.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
return arrayToString("", "");
|
||||
}
|
||||
|
||||
protected String arrayToString(@SuppressWarnings("SameParameterValue") String prefix, @SuppressWarnings("SameParameterValue") String suffix) {
|
||||
StringBuilder sb = new StringBuilder("[").append(prefix).append("".equals(prefix) ? "" : ";");
|
||||
for (int i = 0; i < length(); i++) {
|
||||
sb.append(i == 0 ? "" : ",").append(Array.get(getValue(), i)).append(suffix);
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
60
src/main/java/com/volmit/iris/util/nbt/tag/ByteArrayTag.java
Normal file
60
src/main/java/com/volmit/iris/util/nbt/tag/ByteArrayTag.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ByteArrayTag extends ArrayTag<byte[]> implements Comparable<ByteArrayTag> {
|
||||
|
||||
public static final byte ID = 7;
|
||||
public static final byte[] ZERO_VALUE = new byte[0];
|
||||
|
||||
public ByteArrayTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public ByteArrayTag(byte[] value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && Arrays.equals(getValue(), ((ByteArrayTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ByteArrayTag other) {
|
||||
return Integer.compare(length(), other.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteArrayTag clone() {
|
||||
return new ByteArrayTag(Arrays.copyOf(getValue(), length()));
|
||||
}
|
||||
}
|
||||
65
src/main/java/com/volmit/iris/util/nbt/tag/ByteTag.java
Normal file
65
src/main/java/com/volmit/iris/util/nbt/tag/ByteTag.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
public class ByteTag extends NumberTag<Byte> implements Comparable<ByteTag> {
|
||||
|
||||
public static final byte ID = 1;
|
||||
public static final byte ZERO_VALUE = 0;
|
||||
|
||||
public ByteTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public ByteTag(byte value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
public ByteTag(boolean value) {
|
||||
super((byte) (value ? 1 : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
return getValue() > 0;
|
||||
}
|
||||
|
||||
public void setValue(byte value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asByte() == ((ByteTag) other).asByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ByteTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteTag clone() {
|
||||
return new ByteTag(getValue());
|
||||
}
|
||||
}
|
||||
293
src/main/java/com/volmit/iris/util/nbt/tag/CompoundTag.java
Normal file
293
src/main/java/com/volmit/iris/util/nbt/tag/CompoundTag.java
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class CompoundTag extends Tag<Map<String, Tag<?>>> implements Iterable<Map.Entry<String, Tag<?>>>, Comparable<CompoundTag>, MaxDepthIO {
|
||||
|
||||
public static final byte ID = 10;
|
||||
|
||||
public CompoundTag() {
|
||||
super(createEmptyValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
private static Map<String, Tag<?>> createEmptyValue() {
|
||||
return new KMap<>();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return getValue().size();
|
||||
}
|
||||
|
||||
public Tag<?> remove(String key) {
|
||||
return getValue().remove(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
getValue().clear();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return getValue().containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Tag<?> value) {
|
||||
return getValue().containsValue(value);
|
||||
}
|
||||
|
||||
public Collection<Tag<?>> values() {
|
||||
return getValue().values();
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return getValue().keySet();
|
||||
}
|
||||
|
||||
public Set<Map.Entry<String, Tag<?>>> entrySet() {
|
||||
return new NonNullEntrySet<>(getValue().entrySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, Tag<?>>> iterator() {
|
||||
return entrySet().iterator();
|
||||
}
|
||||
|
||||
public void forEach(BiConsumer<String, Tag<?>> action) {
|
||||
getValue().forEach(action);
|
||||
}
|
||||
|
||||
public <C extends Tag<?>> C get(String key, Class<C> type) {
|
||||
Tag<?> t = getValue().get(key);
|
||||
if (t != null) {
|
||||
return type.cast(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Tag<?> get(String key) {
|
||||
return getValue().get(key);
|
||||
}
|
||||
|
||||
public ByteTag getByteTag(String key) {
|
||||
return get(key, ByteTag.class);
|
||||
}
|
||||
|
||||
public ShortTag getShortTag(String key) {
|
||||
return get(key, ShortTag.class);
|
||||
}
|
||||
|
||||
public IntTag getIntTag(String key) {
|
||||
return get(key, IntTag.class);
|
||||
}
|
||||
|
||||
public LongTag getLongTag(String key) {
|
||||
return get(key, LongTag.class);
|
||||
}
|
||||
|
||||
public FloatTag getFloatTag(String key) {
|
||||
return get(key, FloatTag.class);
|
||||
}
|
||||
|
||||
public DoubleTag getDoubleTag(String key) {
|
||||
return get(key, DoubleTag.class);
|
||||
}
|
||||
|
||||
public StringTag getStringTag(String key) {
|
||||
return get(key, StringTag.class);
|
||||
}
|
||||
|
||||
public ByteArrayTag getByteArrayTag(String key) {
|
||||
return get(key, ByteArrayTag.class);
|
||||
}
|
||||
|
||||
public IntArrayTag getIntArrayTag(String key) {
|
||||
return get(key, IntArrayTag.class);
|
||||
}
|
||||
|
||||
public LongArrayTag getLongArrayTag(String key) {
|
||||
return get(key, LongArrayTag.class);
|
||||
}
|
||||
|
||||
public ListTag<?> getListTag(String key) {
|
||||
return get(key, ListTag.class);
|
||||
}
|
||||
|
||||
public CompoundTag getCompoundTag(String key) {
|
||||
return get(key, CompoundTag.class);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key) {
|
||||
Tag<?> t = get(key);
|
||||
return t instanceof ByteTag && ((ByteTag) t).asByte() > 0;
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
ByteTag t = getByteTag(key);
|
||||
return t == null ? ByteTag.ZERO_VALUE : t.asByte();
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
ShortTag t = getShortTag(key);
|
||||
return t == null ? ShortTag.ZERO_VALUE : t.asShort();
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
IntTag t = getIntTag(key);
|
||||
return t == null ? IntTag.ZERO_VALUE : t.asInt();
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
LongTag t = getLongTag(key);
|
||||
return t == null ? LongTag.ZERO_VALUE : t.asLong();
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
FloatTag t = getFloatTag(key);
|
||||
return t == null ? FloatTag.ZERO_VALUE : t.asFloat();
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
DoubleTag t = getDoubleTag(key);
|
||||
return t == null ? DoubleTag.ZERO_VALUE : t.asDouble();
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
StringTag t = getStringTag(key);
|
||||
return t == null ? StringTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
ByteArrayTag t = getByteArrayTag(key);
|
||||
return t == null ? ByteArrayTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
IntArrayTag t = getIntArrayTag(key);
|
||||
return t == null ? IntArrayTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
LongArrayTag t = getLongArrayTag(key);
|
||||
return t == null ? LongArrayTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public Tag<?> put(String key, Tag<?> tag) {
|
||||
return getValue().put(Objects.requireNonNull(key), Objects.requireNonNull(tag));
|
||||
}
|
||||
|
||||
public Tag<?> putBoolean(String key, boolean value) {
|
||||
return put(key, new ByteTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putByte(String key, byte value) {
|
||||
return put(key, new ByteTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putShort(String key, short value) {
|
||||
return put(key, new ShortTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putInt(String key, int value) {
|
||||
return put(key, new IntTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putLong(String key, long value) {
|
||||
return put(key, new LongTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putFloat(String key, float value) {
|
||||
return put(key, new FloatTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putDouble(String key, double value) {
|
||||
return put(key, new DoubleTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putString(String key, String value) {
|
||||
return put(key, new StringTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putByteArray(String key, byte[] value) {
|
||||
return put(key, new ByteArrayTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putIntArray(String key, int[] value) {
|
||||
return put(key, new IntArrayTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putLongArray(String key, long[] value) {
|
||||
return put(key, new LongArrayTag(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
|
||||
sb.append(first ? "" : ",")
|
||||
.append(escapeString(e.getKey(), false)).append(":")
|
||||
.append(e.getValue().toString(decrementMaxDepth(maxDepth)));
|
||||
first = false;
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other) || size() != ((CompoundTag) other).size()) {
|
||||
return false;
|
||||
}
|
||||
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
|
||||
Tag<?> v;
|
||||
if ((v = ((CompoundTag) other).get(e.getKey())) == null || !e.getValue().equals(v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CompoundTag o) {
|
||||
return Integer.compare(size(), o.getValue().size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag clone() {
|
||||
CompoundTag copy = new CompoundTag();
|
||||
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
|
||||
copy.put(e.getKey(), e.getValue().clone());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
57
src/main/java/com/volmit/iris/util/nbt/tag/DoubleTag.java
Normal file
57
src/main/java/com/volmit/iris/util/nbt/tag/DoubleTag.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
public class DoubleTag extends NumberTag<Double> implements Comparable<DoubleTag> {
|
||||
|
||||
public static final byte ID = 6;
|
||||
public static final double ZERO_VALUE = 0.0D;
|
||||
|
||||
public DoubleTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public DoubleTag(double value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && getValue().equals(((DoubleTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DoubleTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleTag clone() {
|
||||
return new DoubleTag(getValue());
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,34 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Int</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class IntTag extends Tag {
|
||||
public final class EndTag extends Tag<Void> {
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final int value;
|
||||
public static final byte ID = 0;
|
||||
public static final EndTag INSTANCE = new EndTag();
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public IntTag(String name, int value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
private EndTag() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void checkValue(Void value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Int" + append + ": " + value;
|
||||
public String valueToString(int maxDepth) {
|
||||
return "\"end\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndTag clone() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -16,46 +16,42 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
public class FloatTag extends NumberTag<Float> implements Comparable<FloatTag> {
|
||||
|
||||
/**
|
||||
* The <code>TAG_Int_Array</code> tag.
|
||||
*
|
||||
* @author Neil Wightman
|
||||
*/
|
||||
public final class IntArrayTag extends Tag {
|
||||
public static final byte ID = 5;
|
||||
public static final float ZERO_VALUE = 0.0F;
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final int[] value;
|
||||
public FloatTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public IntArrayTag(String name, int[] value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
public FloatTag(float value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getValue() {
|
||||
return value;
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(float value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Int_Array" + append + ": " + Arrays.toString(value);
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && getValue().equals(((FloatTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(FloatTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatTag clone() {
|
||||
return new FloatTag(getValue());
|
||||
}
|
||||
}
|
||||
60
src/main/java/com/volmit/iris/util/nbt/tag/IntArrayTag.java
Normal file
60
src/main/java/com/volmit/iris/util/nbt/tag/IntArrayTag.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IntArrayTag extends ArrayTag<int[]> implements Comparable<IntArrayTag> {
|
||||
|
||||
public static final byte ID = 11;
|
||||
public static final int[] ZERO_VALUE = new int[0];
|
||||
|
||||
public IntArrayTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public IntArrayTag(int[] value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && Arrays.equals(getValue(), ((IntArrayTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(IntArrayTag other) {
|
||||
return Integer.compare(length(), other.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntArrayTag clone() {
|
||||
return new IntArrayTag(Arrays.copyOf(getValue(), length()));
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,42 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Float</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class FloatTag extends Tag {
|
||||
public class IntTag extends NumberTag<Integer> implements Comparable<IntTag> {
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final float value;
|
||||
public static final byte ID = 3;
|
||||
public static final int ZERO_VALUE = 0;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public FloatTag(String name, float value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
public IntTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public IntTag(int value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getValue() {
|
||||
return value;
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(int value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Float" + append + ": " + value;
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asInt() == ((IntTag) other).asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(IntTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntTag clone() {
|
||||
return new IntTag(getValue());
|
||||
}
|
||||
}
|
||||
343
src/main/java/com/volmit/iris/util/nbt/tag/ListTag.java
Normal file
343
src/main/java/com/volmit/iris/util/nbt/tag/ListTag.java
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ListTag represents a typed List in the nbt structure.
|
||||
* An empty {@link ListTag} created using {@link ListTag#createUnchecked(Class)} will be of unknown type
|
||||
* and returns an {@link EndTag}{@code .class} in {@link ListTag#getTypeClass()}.
|
||||
* The type of an empty untyped {@link ListTag} can be set by using any of the {@code add()}
|
||||
* methods or any of the {@code as...List()} methods.
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
public class ListTag<T extends Tag<?>> extends Tag<List<T>> implements Iterable<T>, Comparable<ListTag<T>>, MaxDepthIO {
|
||||
|
||||
public static final byte ID = 9;
|
||||
|
||||
private Class<?> typeClass = null;
|
||||
|
||||
private ListTag() {
|
||||
super(createEmptyValue(3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a non-type-safe ListTag. Its element type will be set after the first
|
||||
* element was added.</p>
|
||||
*
|
||||
* <p>This is an internal helper method for cases where the element type is not known
|
||||
* at construction time. Use {@link #ListTag(Class)} when the type is known.</p>
|
||||
*
|
||||
* @return A new non-type-safe ListTag
|
||||
*/
|
||||
public static ListTag<?> createUnchecked(Class<?> typeClass) {
|
||||
ListTag<?> list = new ListTag<>();
|
||||
list.typeClass = typeClass;
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates an empty mutable list to be used as empty value of ListTags.</p>
|
||||
*
|
||||
* @param <T> Type of the list elements
|
||||
* @param initialCapacity The initial capacity of the returned List
|
||||
* @return An instance of {@link List} with an initial capacity of 3
|
||||
*/
|
||||
private static <T> List<T> createEmptyValue(@SuppressWarnings("SameParameterValue") int initialCapacity) {
|
||||
return new KList<>(initialCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param typeClass The exact class of the elements
|
||||
* @throws IllegalArgumentException When {@code typeClass} is {@link EndTag}{@code .class}
|
||||
* @throws NullPointerException When {@code typeClass} is {@code null}
|
||||
*/
|
||||
public ListTag(Class<? super T> typeClass) throws IllegalArgumentException, NullPointerException {
|
||||
super(createEmptyValue(3));
|
||||
if (typeClass == EndTag.class) {
|
||||
throw new IllegalArgumentException("cannot create ListTag with EndTag elements");
|
||||
}
|
||||
this.typeClass = Objects.requireNonNull(typeClass);
|
||||
}
|
||||
|
||||
public Class<?> getTypeClass() {
|
||||
return typeClass == null ? EndTag.class : typeClass;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return getValue().size();
|
||||
}
|
||||
|
||||
public T remove(int index) {
|
||||
return getValue().remove(index);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
getValue().clear();
|
||||
}
|
||||
|
||||
public boolean contains(T t) {
|
||||
return getValue().contains(t);
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<Tag<?>> tags) {
|
||||
return getValue().containsAll(tags);
|
||||
}
|
||||
|
||||
public void sort(Comparator<T> comparator) {
|
||||
getValue().sort(comparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return getValue().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super T> action) {
|
||||
getValue().forEach(action);
|
||||
}
|
||||
|
||||
public T set(int index, T t) {
|
||||
return getValue().set(index, Objects.requireNonNull(t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Tag to this ListTag after the last index.
|
||||
*
|
||||
* @param t The element to be added.
|
||||
*/
|
||||
public void add(T t) {
|
||||
add(size(), t);
|
||||
}
|
||||
|
||||
public void add(int index, T t) {
|
||||
Objects.requireNonNull(t);
|
||||
if (typeClass == null || typeClass == EndTag.class) {
|
||||
typeClass = t.getClass();
|
||||
} else if (typeClass != t.getClass()) {
|
||||
throw new ClassCastException(
|
||||
String.format("cannot add %s to ListTag<%s>",
|
||||
t.getClass().getSimpleName(),
|
||||
typeClass.getSimpleName()));
|
||||
}
|
||||
getValue().add(index, t);
|
||||
}
|
||||
|
||||
public void addAll(Collection<T> t) {
|
||||
for (T tt : t) {
|
||||
add(tt);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAll(int index, Collection<T> t) {
|
||||
int i = 0;
|
||||
for (T tt : t) {
|
||||
add(index + i, tt);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public void addBoolean(boolean value) {
|
||||
addUnchecked(new ByteTag(value));
|
||||
}
|
||||
|
||||
public void addByte(byte value) {
|
||||
addUnchecked(new ByteTag(value));
|
||||
}
|
||||
|
||||
public void addShort(short value) {
|
||||
addUnchecked(new ShortTag(value));
|
||||
}
|
||||
|
||||
public void addInt(int value) {
|
||||
addUnchecked(new IntTag(value));
|
||||
}
|
||||
|
||||
public void addLong(long value) {
|
||||
addUnchecked(new LongTag(value));
|
||||
}
|
||||
|
||||
public void addFloat(float value) {
|
||||
addUnchecked(new FloatTag(value));
|
||||
}
|
||||
|
||||
public void addDouble(double value) {
|
||||
addUnchecked(new DoubleTag(value));
|
||||
}
|
||||
|
||||
public void addString(String value) {
|
||||
addUnchecked(new StringTag(value));
|
||||
}
|
||||
|
||||
public void addByteArray(byte[] value) {
|
||||
addUnchecked(new ByteArrayTag(value));
|
||||
}
|
||||
|
||||
public void addIntArray(int[] value) {
|
||||
addUnchecked(new IntArrayTag(value));
|
||||
}
|
||||
|
||||
public void addLongArray(long[] value) {
|
||||
addUnchecked(new LongArrayTag(value));
|
||||
}
|
||||
|
||||
public T get(int index) {
|
||||
return getValue().get(index);
|
||||
}
|
||||
|
||||
public int indexOf(T t) {
|
||||
return getValue().indexOf(t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <L extends Tag<?>> ListTag<L> asTypedList(Class<L> type) {
|
||||
checkTypeClass(type);
|
||||
typeClass = type;
|
||||
return (ListTag<L>) this;
|
||||
}
|
||||
|
||||
public ListTag<ByteTag> asByteTagList() {
|
||||
return asTypedList(ByteTag.class);
|
||||
}
|
||||
|
||||
public ListTag<ShortTag> asShortTagList() {
|
||||
return asTypedList(ShortTag.class);
|
||||
}
|
||||
|
||||
public ListTag<IntTag> asIntTagList() {
|
||||
return asTypedList(IntTag.class);
|
||||
}
|
||||
|
||||
public ListTag<LongTag> asLongTagList() {
|
||||
return asTypedList(LongTag.class);
|
||||
}
|
||||
|
||||
public ListTag<FloatTag> asFloatTagList() {
|
||||
return asTypedList(FloatTag.class);
|
||||
}
|
||||
|
||||
public ListTag<DoubleTag> asDoubleTagList() {
|
||||
return asTypedList(DoubleTag.class);
|
||||
}
|
||||
|
||||
public ListTag<StringTag> asStringTagList() {
|
||||
return asTypedList(StringTag.class);
|
||||
}
|
||||
|
||||
public ListTag<ByteArrayTag> asByteArrayTagList() {
|
||||
return asTypedList(ByteArrayTag.class);
|
||||
}
|
||||
|
||||
public ListTag<IntArrayTag> asIntArrayTagList() {
|
||||
return asTypedList(IntArrayTag.class);
|
||||
}
|
||||
|
||||
public ListTag<LongArrayTag> asLongArrayTagList() {
|
||||
return asTypedList(LongArrayTag.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListTag<ListTag<?>> asListTagList() {
|
||||
checkTypeClass(ListTag.class);
|
||||
typeClass = ListTag.class;
|
||||
return (ListTag<ListTag<?>>) this;
|
||||
}
|
||||
|
||||
public ListTag<CompoundTag> asCompoundTagList() {
|
||||
return asTypedList(CompoundTag.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
StringBuilder sb = new StringBuilder("{\"type\":\"").append(getTypeClass().getSimpleName()).append("\",\"list\":[");
|
||||
for (int i = 0; i < size(); i++) {
|
||||
sb.append(i > 0 ? "," : "").append(get(i).valueToString(decrementMaxDepth(maxDepth)));
|
||||
}
|
||||
sb.append("]}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other) || size() != ((ListTag<?>) other).size() || getTypeClass() != ((ListTag<?>) other).getTypeClass()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (!get(i).equals(((ListTag<?>) other).get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getTypeClass().hashCode(), getValue().hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ListTag<T> o) {
|
||||
return Integer.compare(size(), o.getValue().size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ListTag<T> clone() {
|
||||
ListTag<T> copy = new ListTag<>();
|
||||
// assure type safety for clone
|
||||
copy.typeClass = typeClass;
|
||||
for (T t : getValue()) {
|
||||
copy.add((T) t.clone());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
//TODO: make private
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addUnchecked(Tag<?> tag) {
|
||||
if (typeClass != null && typeClass != tag.getClass() && typeClass != EndTag.class) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"cannot add %s to ListTag<%s>",
|
||||
tag.getClass().getSimpleName(), typeClass.getSimpleName()));
|
||||
}
|
||||
add(size(), (T) tag);
|
||||
}
|
||||
|
||||
private void checkTypeClass(Class<?> clazz) {
|
||||
if (typeClass != null && typeClass != EndTag.class && typeClass != clazz) {
|
||||
throw new ClassCastException(String.format(
|
||||
"cannot cast ListTag<%s> to ListTag<%s>",
|
||||
typeClass.getSimpleName(), clazz.getSimpleName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/main/java/com/volmit/iris/util/nbt/tag/LongArrayTag.java
Normal file
60
src/main/java/com/volmit/iris/util/nbt/tag/LongArrayTag.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LongArrayTag extends ArrayTag<long[]> implements Comparable<LongArrayTag> {
|
||||
|
||||
public static final byte ID = 12;
|
||||
public static final long[] ZERO_VALUE = new long[0];
|
||||
|
||||
public LongArrayTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public LongArrayTag(long[] value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && Arrays.equals(getValue(), ((LongArrayTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LongArrayTag other) {
|
||||
return Integer.compare(length(), other.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongArrayTag clone() {
|
||||
return new LongArrayTag(Arrays.copyOf(getValue(), length()));
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,42 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Double</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class DoubleTag extends Tag {
|
||||
public class LongTag extends NumberTag<Long> implements Comparable<LongTag> {
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final double value;
|
||||
public static final byte ID = 4;
|
||||
public static final long ZERO_VALUE = 0L;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public DoubleTag(String name, double value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
public LongTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public LongTag(long value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getValue() {
|
||||
return value;
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(long value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Double" + append + ": " + value;
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asLong() == ((LongTag) other).asLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LongTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongTag clone() {
|
||||
return new LongTag(getValue());
|
||||
}
|
||||
}
|
||||
160
src/main/java/com/volmit/iris/util/nbt/tag/NonNullEntrySet.java
Normal file
160
src/main/java/com/volmit/iris/util/nbt/tag/NonNullEntrySet.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A decorator for the Set returned by CompoundTag#entrySet()
|
||||
* that disallows setting null values.
|
||||
*/
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
class NonNullEntrySet<K, V> implements Set<Map.Entry<K, V>> {
|
||||
|
||||
private final Set<Map.Entry<K, V>> set;
|
||||
|
||||
NonNullEntrySet(Set<Map.Entry<K, V>> set) {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return set.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return set.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new NonNullEntrySetIterator(set.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return set.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return set.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Map.Entry<K, V> kvEntry) {
|
||||
return set.add(kvEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return set.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return set.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Map.Entry<K, V>> c) {
|
||||
return set.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return set.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return set.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
set.clear();
|
||||
}
|
||||
|
||||
class NonNullEntrySetIterator implements Iterator<Map.Entry<K, V>> {
|
||||
|
||||
private final Iterator<Map.Entry<K, V>> iterator;
|
||||
|
||||
NonNullEntrySetIterator(Iterator<Map.Entry<K, V>> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K, V> next() {
|
||||
return new NonNullEntry(iterator.next());
|
||||
}
|
||||
}
|
||||
|
||||
class NonNullEntry implements Map.Entry<K, V> {
|
||||
|
||||
private final Map.Entry<K, V> entry;
|
||||
|
||||
NonNullEntry(Map.Entry<K, V> entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException(getClass().getSimpleName() + " does not allow setting null");
|
||||
}
|
||||
return entry.setValue(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return entry.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return entry.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/main/java/com/volmit/iris/util/nbt/tag/NumberTag.java
Normal file
55
src/main/java/com/volmit/iris/util/nbt/tag/NumberTag.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
public abstract class NumberTag<T extends Number & Comparable<T>> extends Tag<T> {
|
||||
|
||||
public NumberTag(T value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
public byte asByte() {
|
||||
return getValue().byteValue();
|
||||
}
|
||||
|
||||
public short asShort() {
|
||||
return getValue().shortValue();
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
return getValue().intValue();
|
||||
}
|
||||
|
||||
public long asLong() {
|
||||
return getValue().longValue();
|
||||
}
|
||||
|
||||
public float asFloat() {
|
||||
return getValue().floatValue();
|
||||
}
|
||||
|
||||
public double asDouble() {
|
||||
return getValue().doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
return getValue().toString();
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,42 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Short</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class ShortTag extends Tag {
|
||||
public class ShortTag extends NumberTag<Short> implements Comparable<ShortTag> {
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final short value;
|
||||
public static final byte ID = 2;
|
||||
public static final short ZERO_VALUE = 0;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public ShortTag(String name, short value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
public ShortTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public ShortTag(short value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short getValue() {
|
||||
return value;
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(short value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Short" + append + ": " + value;
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asShort() == ((ShortTag) other).asShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ShortTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShortTag clone() {
|
||||
return new ShortTag(getValue());
|
||||
}
|
||||
}
|
||||
68
src/main/java/com/volmit/iris/util/nbt/tag/StringTag.java
Normal file
68
src/main/java/com/volmit/iris/util/nbt/tag/StringTag.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
public class StringTag extends Tag<String> implements Comparable<StringTag> {
|
||||
|
||||
public static final byte ID = 8;
|
||||
public static final String ZERO_VALUE = "";
|
||||
|
||||
public StringTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public StringTag(String value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return super.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
return escapeString(getValue(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && getValue().equals(((StringTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(StringTag o) {
|
||||
return getValue().compareTo(o.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringTag clone() {
|
||||
return new StringTag(getValue());
|
||||
}
|
||||
}
|
||||
219
src/main/java/com/volmit/iris/util/nbt/tag/Tag.java
Normal file
219
src/main/java/com/volmit/iris/util/nbt/tag/Tag.java
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.nbt.tag;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthReachedException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Base class for all NBT tags.
|
||||
*
|
||||
* <h1>Nesting</h1>
|
||||
* <p>All methods serializing instances or deserializing data track the nesting levels to prevent
|
||||
* circular references or malicious data which could, when deserialized, result in thousands
|
||||
* of instances causing a denial of service.</p>
|
||||
*
|
||||
* <p>These methods have a parameter for the maximum nesting depth they are allowed to traverse. A
|
||||
* value of {@code 0} means that only the object itself, but no nested objects may be processed.
|
||||
* If an instance is nested further than allowed, a {@link MaxDepthReachedException} will be thrown.
|
||||
* Providing a negative maximum nesting depth will cause an {@code IllegalArgumentException}
|
||||
* to be thrown.</p>
|
||||
*
|
||||
* <p>Some methods do not provide a parameter to specify the maximum nesting depth, but instead use
|
||||
* {@link #DEFAULT_MAX_DEPTH}, which is also the maximum used by Minecraft. This is documented for
|
||||
* the respective methods.</p>
|
||||
*
|
||||
* <p>If custom NBT tags contain objects other than NBT tags, which can be nested as well, then there
|
||||
* is no guarantee that {@code MaxDepthReachedException}s are thrown for them. The respective class
|
||||
* will document this behavior accordingly.</p>
|
||||
*
|
||||
* @param <T> The type of the contained value
|
||||
*/
|
||||
public abstract class Tag<T> implements Cloneable {
|
||||
|
||||
/**
|
||||
* The default maximum depth of the NBT structure.
|
||||
*/
|
||||
public static final int DEFAULT_MAX_DEPTH = 512;
|
||||
|
||||
private static final Map<String, String> ESCAPE_CHARACTERS;
|
||||
|
||||
static {
|
||||
final Map<String, String> temp = new HashMap<>();
|
||||
temp.put("\\", "\\\\\\\\");
|
||||
temp.put("\n", "\\\\n");
|
||||
temp.put("\t", "\\\\t");
|
||||
temp.put("\r", "\\\\r");
|
||||
temp.put("\"", "\\\\\"");
|
||||
//noinspection Java9CollectionFactory
|
||||
ESCAPE_CHARACTERS = Collections.unmodifiableMap(temp);
|
||||
}
|
||||
|
||||
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[\\\\\n\t\r\"]");
|
||||
private static final Pattern NON_QUOTE_PATTERN = Pattern.compile("[a-zA-Z0-9_\\-+]+");
|
||||
|
||||
private T value;
|
||||
|
||||
/**
|
||||
* Initializes this Tag with some value. If the value is {@code null}, it will
|
||||
* throw a {@code NullPointerException}
|
||||
*
|
||||
* @param value The value to be set for this Tag.
|
||||
*/
|
||||
public Tag(T value) {
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return This Tag's ID, usually used for serialization and deserialization.
|
||||
*/
|
||||
public abstract byte getID();
|
||||
|
||||
/**
|
||||
* @return The value of this Tag.
|
||||
*/
|
||||
protected T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for this Tag directly.
|
||||
*
|
||||
* @param value The value to be set.
|
||||
* @throws NullPointerException If the value is null
|
||||
*/
|
||||
protected void setValue(T value) {
|
||||
this.value = checkValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value {@code value} is {@code null}.
|
||||
*
|
||||
* @param value The value to check
|
||||
* @return The parameter {@code value}
|
||||
* @throws NullPointerException If {@code value} was {@code null}
|
||||
*/
|
||||
protected T checkValue(T value) {
|
||||
return Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link Tag#toString(int)} with an initial depth of {@code 0}.
|
||||
*
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
* @see Tag#toString(int)
|
||||
*/
|
||||
@Override
|
||||
public final String toString() {
|
||||
return toString(DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this Tag in a valid JSON format.
|
||||
*
|
||||
* @param maxDepth The maximum nesting depth.
|
||||
* @return The string representation of this Tag.
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
*/
|
||||
public String toString(int maxDepth) {
|
||||
return "{\"type\":\"" + getClass().getSimpleName() + "\"," +
|
||||
"\"value\":" + valueToString(maxDepth) + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link Tag#valueToString(int)} with {@link Tag#DEFAULT_MAX_DEPTH}.
|
||||
*
|
||||
* @return The string representation of the value of this Tag.
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
*/
|
||||
public String valueToString() {
|
||||
return valueToString(DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON representation of the value of this Tag.
|
||||
*
|
||||
* @param maxDepth The maximum nesting depth.
|
||||
* @return The string representation of the value of this Tag.
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
*/
|
||||
public abstract String valueToString(int maxDepth);
|
||||
|
||||
/**
|
||||
* Returns whether this Tag and some other Tag are equal.
|
||||
* They are equal if {@code other} is not {@code null} and they are of the same class.
|
||||
* Custom Tag implementations should overwrite this but check the result
|
||||
* of this {@code super}-method while comparing.
|
||||
*
|
||||
* @param other The Tag to compare to.
|
||||
* @return {@code true} if they are equal based on the conditions mentioned above.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other != null && getClass() == other.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash code of this Tag. Tags which are equal according to {@link Tag#equals(Object)}
|
||||
* must return an equal hash code.
|
||||
*
|
||||
* @return The hash code of this Tag.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone of this Tag.
|
||||
*
|
||||
* @return A clone of this Tag.
|
||||
*/
|
||||
public abstract Tag<T> clone();
|
||||
|
||||
/**
|
||||
* Escapes a string to fit into a JSON-like string representation for Minecraft
|
||||
* or to create the JSON string representation of a Tag returned from {@link Tag#toString()}
|
||||
*
|
||||
* @param s The string to be escaped.
|
||||
* @param lenient {@code true} if it should force double quotes ({@code "}) at the start and
|
||||
* the end of the string.
|
||||
* @return The escaped string.
|
||||
*/
|
||||
@SuppressWarnings("StringBufferMayBeStringBuilder")
|
||||
protected static String escapeString(String s, @SuppressWarnings("SameParameterValue") boolean lenient) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Matcher m = ESCAPE_PATTERN.matcher(s);
|
||||
while (m.find()) {
|
||||
m.appendReplacement(sb, ESCAPE_CHARACTERS.get(m.group()));
|
||||
}
|
||||
m.appendTail(sb);
|
||||
m = NON_QUOTE_PATTERN.matcher(s);
|
||||
if (!lenient || !m.matches()) {
|
||||
sb.insert(0, "\"").append("\"");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
package com.volmit.iris.util.noise;
|
||||
|
||||
import com.volmit.iris.engine.object.noise.LoaderExpression;
|
||||
import com.volmit.iris.engine.object.noise.IrisExpression;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
|
||||
public class ExpressionNoise implements NoiseGenerator {
|
||||
private final RNG rng;
|
||||
private final LoaderExpression expression;
|
||||
private final IrisExpression expression;
|
||||
|
||||
public ExpressionNoise(RNG rng, LoaderExpression expression) {
|
||||
public ExpressionNoise(RNG rng, IrisExpression expression) {
|
||||
this.rng = rng;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Byte_Array</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class ByteArrayTag extends Tag {
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final byte[] value;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public ByteArrayTag(String name, byte[] value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder hex = new StringBuilder();
|
||||
for (byte b : value) {
|
||||
String hexDigits = Integer.toHexString(b).toUpperCase();
|
||||
if (hexDigits.length() == 1) {
|
||||
hex.append("0");
|
||||
}
|
||||
hex.append(hexDigits).append(" ");
|
||||
}
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_Byte_Array" + append + ": " + hex;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The <code>TAG_Compound</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class CompoundTag extends Tag {
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final Map<String, Tag> value;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public CompoundTag(String name, Map<String, Tag> value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Tag> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
StringBuilder bldr = new StringBuilder();
|
||||
bldr.append("TAG_Compound").append(append).append(": ").append(value.size()).append(" entries\r\n{\r\n");
|
||||
for (Map.Entry<String, Tag> entry : value.entrySet()) {
|
||||
bldr.append(" ").append(entry.getValue().toString().replaceAll("\r\n", "\r\n ")).append("\r\n");
|
||||
}
|
||||
bldr.append("}");
|
||||
return bldr.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The <code>TAG_List</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class ListTag extends Tag {
|
||||
|
||||
/**
|
||||
* The type.
|
||||
*/
|
||||
private final Class<? extends Tag> type;
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final List<Tag> value;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param type The type of item in the list.
|
||||
* @param value The value.
|
||||
*/
|
||||
public ListTag(String name, Class<? extends Tag> type, List<Tag> value) {
|
||||
super(name);
|
||||
this.type = type;
|
||||
this.value = Collections.unmodifiableList(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of item in this list.
|
||||
*
|
||||
* @return The type of item in this list.
|
||||
*/
|
||||
public Class<? extends Tag> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tag> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
StringBuilder bldr = new StringBuilder();
|
||||
bldr.append("TAG_List").append(append).append(": ").append(value.size()).append(" entries of type ").append(NBTUtils.getTypeName(type)).append("\r\n{\r\n");
|
||||
for (Tag t : value) {
|
||||
bldr.append(" ").append(t.toString().replaceAll("\r\n", "\r\n ")).append("\r\n");
|
||||
}
|
||||
bldr.append("}");
|
||||
return bldr.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/*
|
||||
Changes : Neil Wightman - Support 19133 Tag_Int_Array tag
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class which holds constant values.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class NBTConstants {
|
||||
|
||||
/**
|
||||
* The character set used by NBT (UTF-8).
|
||||
*/
|
||||
public static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Tag type constants.
|
||||
*/
|
||||
public static final int TYPE_END = 0,
|
||||
TYPE_BYTE = 1,
|
||||
TYPE_SHORT = 2,
|
||||
TYPE_INT = 3,
|
||||
TYPE_LONG = 4,
|
||||
TYPE_FLOAT = 5,
|
||||
TYPE_DOUBLE = 6,
|
||||
TYPE_BYTE_ARRAY = 7,
|
||||
TYPE_STRING = 8,
|
||||
TYPE_LIST = 9,
|
||||
TYPE_COMPOUND = 10,
|
||||
TYPE_INT_ARRAY = 11;
|
||||
|
||||
/**
|
||||
* Default private constructor.
|
||||
*/
|
||||
private NBTConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/*
|
||||
Changes :
|
||||
Neil Wightman - Support 19133 Tag_Int_Array tag
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This class reads <strong>NBT</strong>, or
|
||||
* <strong>Named Binary Tag</strong> streams, and produces an object graph of subclasses of the <code>Tag</code> object.</p>
|
||||
*
|
||||
* <p>
|
||||
* The NBT format was created by Markus Persson, and the specification may be found at <a href="http://www.minecraft.net/docs/NBT.txt">
|
||||
* http://www.minecraft.net/docs/NBT.txt</a>.</p>
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class NBTInputStream implements Closeable {
|
||||
|
||||
/**
|
||||
* The data input stream.
|
||||
*/
|
||||
private final DataInputStream is;
|
||||
|
||||
/**
|
||||
* Create a new <code>NBTInputStream</code>, which will source its data from the specified input stream.
|
||||
*
|
||||
* @param is The output stream
|
||||
*/
|
||||
public NBTInputStream(DataInputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>NBTInputStream</code>, which will source its data from the specified input stream.
|
||||
* The stream will be decompressed using GZIP.
|
||||
*
|
||||
* @param is The input stream.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public NBTInputStream(InputStream is) throws IOException {
|
||||
this.is = new DataInputStream(new GZIPInputStream(is));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an NBT tag from the stream.
|
||||
*
|
||||
* @return The tag that was read.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public Tag readTag() throws IOException {
|
||||
return readTag(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an NBT from the stream.
|
||||
*
|
||||
* @param depth The depth of this tag.
|
||||
* @return The tag that was read.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private Tag readTag(int depth) throws IOException {
|
||||
int type = is.readByte() & 0xFF;
|
||||
|
||||
String name;
|
||||
if (type != NBTConstants.TYPE_END) {
|
||||
int nameLength = is.readShort() & 0xFFFF;
|
||||
byte[] nameBytes = new byte[nameLength];
|
||||
is.readFully(nameBytes);
|
||||
name = new String(nameBytes, NBTConstants.CHARSET);
|
||||
} else {
|
||||
name = "";
|
||||
}
|
||||
|
||||
return readTagPayload(type, name, depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the payload of a tag, given the name and type.
|
||||
*
|
||||
* @param type The type.
|
||||
* @param name The name.
|
||||
* @param depth The depth.
|
||||
* @return The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private Tag readTagPayload(int type, String name, int depth) throws IOException {
|
||||
switch (type) {
|
||||
case NBTConstants.TYPE_END:
|
||||
if (depth == 0) {
|
||||
throw new IOException("TAG_End found without a TAG_Compound/TAG_List tag preceding it.");
|
||||
} else {
|
||||
return new EndTag();
|
||||
}
|
||||
case NBTConstants.TYPE_BYTE:
|
||||
return new ByteTag(name, is.readByte());
|
||||
case NBTConstants.TYPE_SHORT:
|
||||
return new ShortTag(name, is.readShort());
|
||||
case NBTConstants.TYPE_INT:
|
||||
return new IntTag(name, is.readInt());
|
||||
case NBTConstants.TYPE_LONG:
|
||||
return new LongTag(name, is.readLong());
|
||||
case NBTConstants.TYPE_FLOAT:
|
||||
return new FloatTag(name, is.readFloat());
|
||||
case NBTConstants.TYPE_DOUBLE:
|
||||
return new DoubleTag(name, is.readDouble());
|
||||
case NBTConstants.TYPE_BYTE_ARRAY:
|
||||
int length = is.readInt();
|
||||
byte[] bytes = new byte[length];
|
||||
is.readFully(bytes);
|
||||
return new ByteArrayTag(name, bytes);
|
||||
case NBTConstants.TYPE_STRING:
|
||||
length = is.readShort();
|
||||
bytes = new byte[length];
|
||||
is.readFully(bytes);
|
||||
return new StringTag(name, new String(bytes, NBTConstants.CHARSET));
|
||||
case NBTConstants.TYPE_LIST:
|
||||
int childType = is.readByte();
|
||||
length = is.readInt();
|
||||
|
||||
List<Tag> tagList = new ArrayList<>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
Tag tag = readTagPayload(childType, "", depth + 1);
|
||||
if (tag instanceof EndTag) {
|
||||
throw new IOException("TAG_End not permitted in a list.");
|
||||
}
|
||||
tagList.add(tag);
|
||||
}
|
||||
|
||||
return new ListTag(name, NBTUtils.getTypeClass(childType), tagList);
|
||||
case NBTConstants.TYPE_COMPOUND:
|
||||
Map<String, Tag> tagMap = new HashMap<>();
|
||||
while (true) {
|
||||
Tag tag = readTag(depth + 1);
|
||||
if (tag instanceof EndTag) {
|
||||
break;
|
||||
} else {
|
||||
tagMap.put(tag.getName(), tag);
|
||||
}
|
||||
}
|
||||
|
||||
return new CompoundTag(name, tagMap);
|
||||
case NBTConstants.TYPE_INT_ARRAY:
|
||||
length = is.readInt();
|
||||
int[] value = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
value[i] = is.readInt();
|
||||
}
|
||||
return new IntArrayTag(name, value);
|
||||
default:
|
||||
throw new IOException("Invalid tag type: " + type + ".");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
is.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
/*
|
||||
Changes : Neil Wightman - Support 19133 Tag_Int_Array tag
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This class writes <strong>NBT</strong>, or
|
||||
* <strong>Named Binary Tag</strong> <code>Tag</code> objects to an underlying <code>OutputStream</code>.</p>
|
||||
*
|
||||
* <p>
|
||||
* The NBT format was created by Markus Persson, and the specification may be found at <a href="http://www.minecraft.net/docs/NBT.txt">
|
||||
* http://www.minecraft.net/docs/NBT.txt</a>.</p>
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
@SuppressWarnings({"EmptyMethod", "JavaDoc"})
|
||||
public final class NBTOutputStream implements Closeable {
|
||||
|
||||
/**
|
||||
* The output stream.
|
||||
*/
|
||||
private final DataOutputStream os;
|
||||
|
||||
/**
|
||||
* Create a new <code>NBTOutputStream</code>, which will write data to the specified underlying output stream.
|
||||
*
|
||||
* @param os The output stream
|
||||
*/
|
||||
public NBTOutputStream(DataOutputStream os) {
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>NBTOutputStream</code>, which will write data to the specified underlying output stream.
|
||||
* the stream will be compressed using GZIP.
|
||||
*
|
||||
* @param os The output stream.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public NBTOutputStream(OutputStream os) throws IOException {
|
||||
this.os = new DataOutputStream(new GZIPOutputStream(os));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a tag.
|
||||
*
|
||||
* @param tag The tag to write.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void writeTag(Tag tag) throws IOException {
|
||||
int type = NBTUtils.getTypeCode(tag.getClass());
|
||||
String name = tag.getName();
|
||||
byte[] nameBytes = name.getBytes(NBTConstants.CHARSET);
|
||||
|
||||
os.writeByte(type);
|
||||
os.writeShort(nameBytes.length);
|
||||
os.write(nameBytes);
|
||||
|
||||
if (type == NBTConstants.TYPE_END) {
|
||||
throw new IOException("Named TAG_End not permitted.");
|
||||
}
|
||||
|
||||
writeTagPayload(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes tag payload.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeTagPayload(Tag tag) throws IOException {
|
||||
int type = NBTUtils.getTypeCode(tag.getClass());
|
||||
switch (type) {
|
||||
case NBTConstants.TYPE_END -> writeEndTagPayload((EndTag) tag);
|
||||
case NBTConstants.TYPE_BYTE -> writeByteTagPayload((ByteTag) tag);
|
||||
case NBTConstants.TYPE_SHORT -> writeShortTagPayload((ShortTag) tag);
|
||||
case NBTConstants.TYPE_INT -> writeIntTagPayload((IntTag) tag);
|
||||
case NBTConstants.TYPE_LONG -> writeLongTagPayload((LongTag) tag);
|
||||
case NBTConstants.TYPE_FLOAT -> writeFloatTagPayload((FloatTag) tag);
|
||||
case NBTConstants.TYPE_DOUBLE -> writeDoubleTagPayload((DoubleTag) tag);
|
||||
case NBTConstants.TYPE_BYTE_ARRAY -> writeByteArrayTagPayload((ByteArrayTag) tag);
|
||||
case NBTConstants.TYPE_STRING -> writeStringTagPayload((StringTag) tag);
|
||||
case NBTConstants.TYPE_LIST -> writeListTagPayload((ListTag) tag);
|
||||
case NBTConstants.TYPE_COMPOUND -> writeCompoundTagPayload((CompoundTag) tag);
|
||||
case NBTConstants.TYPE_INT_ARRAY -> writeIntArrayTagPayload((IntArrayTag) tag);
|
||||
default -> throw new IOException("Invalid tag type: " + type + ".");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Byte</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeByteTagPayload(ByteTag tag) throws IOException {
|
||||
os.writeByte(tag.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Byte_Array</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeByteArrayTagPayload(ByteArrayTag tag) throws IOException {
|
||||
byte[] bytes = tag.getValue();
|
||||
os.writeInt(bytes.length);
|
||||
os.write(bytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Compound</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeCompoundTagPayload(CompoundTag tag) throws IOException {
|
||||
for (Tag childTag : tag.getValue().values()) {
|
||||
writeTag(childTag);
|
||||
}
|
||||
os.writeByte((byte) 0); // end tag - better way?
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_List</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeListTagPayload(ListTag tag) throws IOException {
|
||||
Class<? extends Tag> clazz = tag.getType();
|
||||
List<Tag> tags = tag.getValue();
|
||||
int size = tags.size();
|
||||
|
||||
os.writeByte(NBTUtils.getTypeCode(clazz));
|
||||
os.writeInt(size);
|
||||
for (Tag value : tags) {
|
||||
writeTagPayload(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_String</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeStringTagPayload(StringTag tag) throws IOException {
|
||||
byte[] bytes = tag.getValue().getBytes(NBTConstants.CHARSET);
|
||||
os.writeShort(bytes.length);
|
||||
os.write(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Double</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeDoubleTagPayload(DoubleTag tag) throws IOException {
|
||||
os.writeDouble(tag.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Float</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeFloatTagPayload(FloatTag tag) throws IOException {
|
||||
os.writeFloat(tag.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Long</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeLongTagPayload(LongTag tag) throws IOException {
|
||||
os.writeLong(tag.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Int</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeIntTagPayload(IntTag tag) throws IOException {
|
||||
os.writeInt(tag.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Short</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeShortTagPayload(ShortTag tag) throws IOException {
|
||||
os.writeShort(tag.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Empty</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeEndTagPayload(EndTag tag) {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a <code>TAG_Int_Array</code> tag.
|
||||
*
|
||||
* @param tag The tag.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void writeIntArrayTagPayload(IntArrayTag tag) throws IOException {
|
||||
final int[] values = tag.getValue();
|
||||
os.writeInt(values.length);
|
||||
for (final int value : values) {
|
||||
os.writeInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
os.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
/*
|
||||
Changes : Neil Wightman - Support 19133 Tag_Int_Array tag
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class which contains NBT-related utility methods. This currently supports reading 19133 but <b>only</b> writing 19132.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class NBTUtils {
|
||||
|
||||
/**
|
||||
* Gets the type name of a tag.
|
||||
*
|
||||
* @param clazz The tag class.
|
||||
* @return The type name.
|
||||
*/
|
||||
public static String getTypeName(Class<? extends Tag> clazz) {
|
||||
if (clazz.equals(ByteArrayTag.class)) {
|
||||
return "TAG_Byte_Array";
|
||||
} else if (clazz.equals(ByteTag.class)) {
|
||||
return "TAG_Byte";
|
||||
} else if (clazz.equals(CompoundTag.class)) {
|
||||
return "TAG_Compound";
|
||||
} else if (clazz.equals(DoubleTag.class)) {
|
||||
return "TAG_Double";
|
||||
} else if (clazz.equals(EndTag.class)) {
|
||||
return "TAG_End";
|
||||
} else if (clazz.equals(FloatTag.class)) {
|
||||
return "TAG_Float";
|
||||
} else if (clazz.equals(IntTag.class)) {
|
||||
return "TAG_Int";
|
||||
} else if (clazz.equals(ListTag.class)) {
|
||||
return "TAG_List";
|
||||
} else if (clazz.equals(LongTag.class)) {
|
||||
return "TAG_Long";
|
||||
} else if (clazz.equals(ShortTag.class)) {
|
||||
return "TAG_Short";
|
||||
} else if (clazz.equals(StringTag.class)) {
|
||||
return "TAG_String";
|
||||
} else if (clazz.equals(IntArrayTag.class)) {
|
||||
return "TAG_Int_Array";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ").");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type code of a tag class.
|
||||
*
|
||||
* @param clazz The tag class.
|
||||
* @return The type code.
|
||||
* @throws IllegalArgumentException if the tag class is invalid.
|
||||
*/
|
||||
public static int getTypeCode(Class<? extends Tag> clazz) {
|
||||
if (clazz.equals(ByteArrayTag.class)) {
|
||||
return NBTConstants.TYPE_BYTE_ARRAY;
|
||||
} else if (clazz.equals(ByteTag.class)) {
|
||||
return NBTConstants.TYPE_BYTE;
|
||||
} else if (clazz.equals(CompoundTag.class)) {
|
||||
return NBTConstants.TYPE_COMPOUND;
|
||||
} else if (clazz.equals(DoubleTag.class)) {
|
||||
return NBTConstants.TYPE_DOUBLE;
|
||||
} else if (clazz.equals(EndTag.class)) {
|
||||
return NBTConstants.TYPE_END;
|
||||
} else if (clazz.equals(FloatTag.class)) {
|
||||
return NBTConstants.TYPE_FLOAT;
|
||||
} else if (clazz.equals(IntTag.class)) {
|
||||
return NBTConstants.TYPE_INT;
|
||||
} else if (clazz.equals(ListTag.class)) {
|
||||
return NBTConstants.TYPE_LIST;
|
||||
} else if (clazz.equals(LongTag.class)) {
|
||||
return NBTConstants.TYPE_LONG;
|
||||
} else if (clazz.equals(ShortTag.class)) {
|
||||
return NBTConstants.TYPE_SHORT;
|
||||
} else if (clazz.equals(StringTag.class)) {
|
||||
return NBTConstants.TYPE_STRING;
|
||||
} else if (clazz.equals(IntArrayTag.class)) {
|
||||
return NBTConstants.TYPE_INT_ARRAY;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ").");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of a type of tag.
|
||||
*
|
||||
* @param type The type.
|
||||
* @return The class.
|
||||
* @throws IllegalArgumentException if the tag type is invalid.
|
||||
*/
|
||||
public static Class<? extends Tag> getTypeClass(int type) {
|
||||
return switch (type) {
|
||||
case NBTConstants.TYPE_END -> EndTag.class;
|
||||
case NBTConstants.TYPE_BYTE -> ByteTag.class;
|
||||
case NBTConstants.TYPE_SHORT -> ShortTag.class;
|
||||
case NBTConstants.TYPE_INT -> IntTag.class;
|
||||
case NBTConstants.TYPE_LONG -> LongTag.class;
|
||||
case NBTConstants.TYPE_FLOAT -> FloatTag.class;
|
||||
case NBTConstants.TYPE_DOUBLE -> DoubleTag.class;
|
||||
case NBTConstants.TYPE_BYTE_ARRAY -> ByteArrayTag.class;
|
||||
case NBTConstants.TYPE_STRING -> StringTag.class;
|
||||
case NBTConstants.TYPE_LIST -> ListTag.class;
|
||||
case NBTConstants.TYPE_COMPOUND -> CompoundTag.class;
|
||||
case NBTConstants.TYPE_INT_ARRAY -> IntArrayTag.class;
|
||||
default -> throw new IllegalArgumentException("Invalid tag type : " + type + ".");
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Default private constructor.
|
||||
*/
|
||||
private NBTUtils() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.util.oldnbt;
|
||||
|
||||
/**
|
||||
* The <code>TAG_String</code> tag.
|
||||
*
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public final class StringTag extends Tag {
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Creates the tag.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public StringTag(String name, String value) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
String append = "";
|
||||
if (name != null && !name.equals("")) {
|
||||
append = "(\"" + this.getName() + "\")";
|
||||
}
|
||||
return "TAG_String" + append + ": " + value;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user