mirror of https://github.com/webbukkit/dynmap.git
321 lines
12 KiB
Java
321 lines
12 KiB
Java
package org.dynmap.renderer;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
|
|
// This represents a distinct block state value for a simple block from the world data.
|
|
// Each distinct persistent block state from the world data will map to exactly one instance of this class, such that
|
|
// for a given distinct world data value (blockID:meta for pre 1.13, blockstateid for 1.13+) will map to a specific instance
|
|
// of this class. In this way, if block state X and block state Y are the same, they can be compared by (X = Y), versus
|
|
// needing the cost of an equals() or similar comparator. Also, use in IdentityHashMaps and the like will be enabled.
|
|
public class DynmapBlockState {
|
|
// Block state index (0-based offset of state vs first state for given block type)
|
|
public final int stateIndex;
|
|
// Base block state (state index = 0) - first state for block corresponding to the block for this state
|
|
public final DynmapBlockState baseState;
|
|
// Block name (minecraft:name for vanilla, modid:name for custom)
|
|
public final String blockName;
|
|
// Block state string (attrib=value, attrib=value, etc for 1.13+, meta=value for 1.12 or earlier)
|
|
public final String stateName;
|
|
// Overall state index (uniquely assigned autoincrement number for state: packed, zero based)
|
|
public final int globalStateIndex;
|
|
// List of block states (only defined on base block), indexed by stateIndex (null if single state base block)
|
|
private DynmapBlockState[] states;
|
|
// Full name for state (base name, or base name[state name])
|
|
private final String fullName;
|
|
// Next global state index
|
|
private static int nextGlobalStateIndex = 0;
|
|
// Match flags
|
|
private int matchflags;
|
|
private static int MATCH_AIR = 1 << 0;
|
|
private static int MATCH_WATER = 1 << 1;
|
|
private static int MATCH_SNOW = 1 << 2;
|
|
private static int MATCH_LOG = 1 << 3;
|
|
private static int MATCH_GRASS = 1 << 4;
|
|
private static int MATCH_WATERLOGGED = 1 << 5;
|
|
private static int MATCH_LEAVES = 1 << 6;
|
|
|
|
// Map of base blocks by name
|
|
private static HashMap<String, DynmapBlockState> blocksByName = new HashMap<String, DynmapBlockState>();
|
|
// Map of states by global state index
|
|
private static HashMap<Integer, DynmapBlockState> blocksByIndex = new HashMap<Integer, DynmapBlockState>();
|
|
|
|
// Well known block names (some versions might need to overwrite these)
|
|
public static String AIR_BLOCK = "minecraft:air";
|
|
public static String STONE_BLOCK = "minecraft:stone";
|
|
public static String GRASS_BLOCK = "minecraft:grass";
|
|
public static String GOLD_ORE_BLOCK = "minecraft:gold_ore";
|
|
public static String IRON_ORE_BLOCK = "minecraft:iron_ore";
|
|
public static String COAL_ORE_BLOCK = "minecraft:coal_ore";
|
|
public static String LAPIS_ORE_BLOCK = "minecraft:lapis_ore";
|
|
public static String DIAMOND_ORE_BLOCK = "minecraft:diamond_ore";
|
|
public static String REDSTONE_ORE_BLOCK = "minecraft:redstone_ore";
|
|
public static String LIT_REDSTONE_ORE_BLOCK = "minecraft:lit_redstone_ore";
|
|
public static String EMERALD_ORE_BLOCK = "minecraft:emerald_ore";
|
|
public static String QUARTZ_ORE_BLOCK = "minecraft:quartz_ore";
|
|
public static String LOG_BLOCK = "minecraft:log";
|
|
public static String LOG2_BLOCK = "minecraft:log2";
|
|
public static String LEAVES_BLOCK = "minecraft:leaves";
|
|
public static String LEAVES2_BLOCK = "minecraft:leaves2";
|
|
public static String GLASS_BLOCK = "minecraft:glass";
|
|
public static String WOODEN_DOOR_BLOCK = "minecraft:wooden_door";
|
|
public static String IRON_DOOR_BLOCK = "minecraft:iron_door";
|
|
public static String SNOW_LAYER_BLOCK = "minecraft:snow_layer";
|
|
public static String SNOW_BLOCK = "minecraft:snow";
|
|
public static String ICE_BLOCK = "minecraft:ice";
|
|
public static String QUARTZ_BLOCK = "minecraft:quartz_block";
|
|
public static String WATER_BLOCK = "minecraft:water";
|
|
public static String FLOWING_WATER_BLOCK = "minecraft:flowing_water";
|
|
|
|
/** Names of log blocks: mod versions will need to add to this */
|
|
private static HashSet<String> log_blocks = new HashSet<String>(Arrays.asList(LOG_BLOCK, LOG2_BLOCK));
|
|
/** Names of water blocks: mod versions will need to add to this */
|
|
private static HashSet<String> water_blocks = new HashSet<String>(Arrays.asList(WATER_BLOCK, FLOWING_WATER_BLOCK));
|
|
|
|
// Well known base blocks - air
|
|
public static final DynmapBlockState AIR = new DynmapBlockState(null, 0, AIR_BLOCK, "");
|
|
|
|
/**
|
|
* Constructor for block state
|
|
* @param base - base block state (null if first/only state for block)
|
|
* @param stateidx - index of state (0-based relative to the base block state)
|
|
* @param blkname - block name, in modid:blockname format (minecraft:blockname for vanilla)
|
|
* @param statename - block state name: null if single state block, "attrib=value,..." for 1.13+, "meta=value" for 1.12-
|
|
*/
|
|
public DynmapBlockState(DynmapBlockState base, int stateidx, String blkname, String statename) {
|
|
globalStateIndex = (nextGlobalStateIndex++); // Assign index
|
|
if (base == null) base = this;
|
|
baseState = base;
|
|
stateIndex = stateidx;
|
|
if (blkname.indexOf(':') == -1) { // No mod:, assume minecraft:
|
|
blkname = "minecraft:" + blkname;
|
|
}
|
|
blockName = blkname;
|
|
stateName = (statename != null) ? statename : "";
|
|
if (base != this) { // If we aren't base block state
|
|
if (base.states == null) { // If no state list yet
|
|
base.states = new DynmapBlockState[stateidx+1]; // Enough for us to fit
|
|
Arrays.fill(base.states, AIR);
|
|
base.states[0] = base; // Add base state as index 0
|
|
}
|
|
else if (base.states.length <= stateidx) { // Not enough room
|
|
// Resize it
|
|
DynmapBlockState[] newstates = new DynmapBlockState[stateidx+1];
|
|
System.arraycopy(base.states, 0, newstates, 0, base.states.length);
|
|
Arrays.fill(newstates, base.states.length, stateidx+1, AIR);
|
|
base.states = newstates;
|
|
}
|
|
base.states[stateidx] = this;
|
|
}
|
|
// If base block state, add to map
|
|
if (base == this) {
|
|
blocksByName.put(blkname, this);
|
|
}
|
|
if (stateName.length() > 0) {
|
|
fullName = blockName + "[" + stateName + "]";
|
|
}
|
|
else {
|
|
fullName = blockName;
|
|
}
|
|
// Add to lookup by global ID
|
|
blocksByIndex.put(globalStateIndex, this);
|
|
// Precompute match flags
|
|
matchflags |= blockName.equals(AIR_BLOCK) ? MATCH_AIR : 0;
|
|
matchflags |= isWater(blockName) ? MATCH_WATER : 0;
|
|
matchflags |= (blockName.equals(SNOW_BLOCK) || blockName.equals(SNOW_LAYER_BLOCK)) ? MATCH_SNOW : 0;
|
|
matchflags |= blockName.equals(GRASS_BLOCK) ? MATCH_GRASS : 0;
|
|
matchflags |= log_blocks.contains(blockName) ? MATCH_LOG : 0;
|
|
}
|
|
/**
|
|
* Get state for same base block with given index
|
|
* @param idx - index number
|
|
* @return new state, or AIR if invalid index
|
|
*/
|
|
public final DynmapBlockState getStateByIndex(int idx) {
|
|
if (baseState.states == null) {
|
|
return (idx == 0) ? this : null;
|
|
}
|
|
return ((idx >= 0) && (idx < baseState.states.length)) ? baseState.states[idx] : DynmapBlockState.AIR;
|
|
}
|
|
/**
|
|
* Find base block state by block name
|
|
* @param name - block name (modid:name)
|
|
* @return base block state, or AIR if not found
|
|
*/
|
|
public static final DynmapBlockState getBaseStateByName(String name) {
|
|
DynmapBlockState blk = blocksByName.get(name);
|
|
if ((blk == null) && (name.indexOf(':') == -1)) {
|
|
blk = blocksByName.get("minecraft:" + name);
|
|
}
|
|
return (blk != null) ? blk : AIR;
|
|
}
|
|
/**
|
|
* Find block state by block name and state index
|
|
* @param name - block name (modid:name)
|
|
* @param idx - state index
|
|
* @return base block state, or AIR if not found
|
|
*/
|
|
public static final DynmapBlockState getStateByNameAndIndex(String name, int idx) {
|
|
DynmapBlockState blk = getBaseStateByName(name);
|
|
if (blk != null) {
|
|
blk = blk.getState(idx);
|
|
}
|
|
return (blk != null) ? blk : AIR;
|
|
}
|
|
/**
|
|
* Find block state by global index
|
|
* @param gidx - global index
|
|
* @return block state, or AIR if not found
|
|
*/
|
|
public static final DynmapBlockState getStateByGlobalIndex(int gidx) {
|
|
DynmapBlockState bs = blocksByIndex.get(gidx);
|
|
return (bs != null) ? bs : AIR;
|
|
}
|
|
/**
|
|
* Get current top of range of block state global indexes, plus 1
|
|
* @return length of global block state index range (N, for 0-(N-1))
|
|
*/
|
|
public static final int getGlobalIndexMax() {
|
|
return nextGlobalStateIndex;
|
|
}
|
|
/**
|
|
* Return true if block is not air
|
|
* @return true if not air, false if air
|
|
*/
|
|
public final boolean isNotAir() {
|
|
return (matchflags & MATCH_AIR) == 0;
|
|
}
|
|
/**
|
|
* Return true if block is air
|
|
* @return true if air, false if air
|
|
*/
|
|
public final boolean isAir() {
|
|
return (matchflags & MATCH_AIR) != 0;
|
|
}
|
|
/**
|
|
* Set to air
|
|
*/
|
|
public final void setAir() {
|
|
matchflags |= MATCH_AIR;
|
|
}
|
|
/**
|
|
* Return number of states under base state
|
|
* @return state count
|
|
*/
|
|
public final int getStateCount() {
|
|
if (baseState.states == null) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return baseState.states.length;
|
|
}
|
|
}
|
|
/**
|
|
* Get nth state index within base block state
|
|
* @param idx - state index
|
|
* @return state, or null if not defined
|
|
*/
|
|
public final DynmapBlockState getState(int idx) {
|
|
if (baseState.states == null) {
|
|
return (idx == 0) ? this : AIR;
|
|
}
|
|
else {
|
|
return (idx < baseState.states.length) ? baseState.states[idx] : AIR;
|
|
}
|
|
}
|
|
/**
|
|
* Test if block is log block
|
|
*/
|
|
public final boolean isLog() {
|
|
return (matchflags & MATCH_LOG) != 0;
|
|
}
|
|
/**
|
|
* Set to log block
|
|
*/
|
|
public final void setLog() {
|
|
matchflags |= MATCH_LOG;
|
|
}
|
|
/**
|
|
* Test if block is water block
|
|
*/
|
|
public final boolean isWater() {
|
|
return (matchflags & MATCH_WATER) != 0;
|
|
}
|
|
/**
|
|
* Test if block name is water block
|
|
*/
|
|
public static boolean isWater(String blockname) {
|
|
return water_blocks.contains(blockname);
|
|
}
|
|
/**
|
|
* Add water block name
|
|
* @param name - name of custom water block
|
|
*/
|
|
public void addWaterBlock(String name) {
|
|
water_blocks.add(name);
|
|
// Apply to existing blocks
|
|
DynmapBlockState bbs = DynmapBlockState.getBaseStateByName(name);
|
|
if (bbs.isNotAir()) {
|
|
for (int i = 0; i < bbs.getStateCount(); i++) {
|
|
bbs.states[i].matchflags |= MATCH_WATER;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Test if block is snow block
|
|
*/
|
|
public final boolean isSnow() {
|
|
return (matchflags & MATCH_SNOW) != 0;
|
|
}
|
|
/**
|
|
* Test if block is grass block
|
|
*/
|
|
public final boolean isGrass() {
|
|
return (matchflags & MATCH_GRASS) != 0;
|
|
}
|
|
/**
|
|
* Test if block is waterlogged (in block filled with water)
|
|
*/
|
|
public final boolean isWaterlogged() {
|
|
return (matchflags & MATCH_WATERLOGGED) != 0;
|
|
}
|
|
/**
|
|
* Set state to be waterlogged (block filled with water)
|
|
*/
|
|
public final void setWaterlogged() {
|
|
matchflags |= MATCH_WATERLOGGED;
|
|
}
|
|
/**
|
|
* Test if block is leaves
|
|
*/
|
|
public final boolean isLeaves() {
|
|
return (matchflags & MATCH_LEAVES) != 0;
|
|
}
|
|
/**
|
|
* Set state to be leaves
|
|
*/
|
|
public final void setLeaves() {
|
|
matchflags |= MATCH_LEAVES;
|
|
}
|
|
/**
|
|
* Test for matching blockname
|
|
*/
|
|
public boolean is(String blkname) {
|
|
return blockName.equals(blkname);
|
|
}
|
|
/**
|
|
* Test for matching base state
|
|
*/
|
|
public boolean matchingBaseState(DynmapBlockState blk) {
|
|
return this.baseState == blk.baseState;
|
|
}
|
|
/**
|
|
* To printable string
|
|
*/
|
|
@Override
|
|
public String toString() {
|
|
return fullName;
|
|
}
|
|
}
|