Add blockIdMapping supported by forges block-id registry in the level.dat file of the world

This commit is contained in:
Blue (Lukas Rieger) 2020-01-08 00:37:30 +01:00
parent a6bf0ae2ca
commit 5168bfcc05
8 changed files with 164 additions and 48 deletions

View File

@ -38,7 +38,8 @@ import ninja.leaping.configurate.loader.ConfigurationLoader;
public class BlockIdConfig implements BlockIdMapper {
private ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private Map<BlockIDMeta, BlockState> mappings;
private Map<BlockNumeralIDMeta, BlockState> numeralMappings;
private Map<BlockIDMeta, BlockState> idMappings;
public BlockIdConfig(ConfigurationNode node) {
this(node, null);
@ -47,29 +48,39 @@ public class BlockIdConfig implements BlockIdMapper {
public BlockIdConfig(ConfigurationNode node, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.autopoulationConfigLoader = autopoulationConfigLoader;
mappings = new HashMap<>();
numeralMappings = new HashMap<>();
idMappings = new HashMap<>();
for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){
String key = e.getKey().toString();
String value = e.getValue().getString();
try {
int splitIndex = key.indexOf(':');
int blockId, blockMeta;
if (splitIndex > 0 && splitIndex < key.length() - 1) {
blockId = Integer.parseInt(key.substring(0, splitIndex));
blockMeta = Integer.parseInt(key.substring(splitIndex + 1));
} else {
blockId = Integer.parseInt(key);
blockMeta = 0;
int splitIndex = key.lastIndexOf(':');
if (splitIndex <= 0 || splitIndex >= key.length() - 1) {
Logger.global.logWarning("Loading BlockIdConfig: Failed to parse blockid:meta from key '" + key + "'");
continue;
}
BlockIDMeta idmeta = new BlockIDMeta(blockId, blockMeta);
BlockState state = BlockState.fromString(value);
if (blockId == 0) state = BlockState.AIR; //use the static field to increase render speed (== comparison)
String blockId = key.substring(0, splitIndex);
int blockNumeralId;
try {
blockNumeralId = Integer.parseInt(blockId);
} catch (NumberFormatException ex) {
blockNumeralId = -1;
}
int blockMeta = Integer.parseInt(key.substring(splitIndex + 1));
BlockState state = BlockState.fromString(value);
mappings.put(idmeta, state);
if (blockNumeralId >= 0) {
BlockNumeralIDMeta idmeta = new BlockNumeralIDMeta(blockNumeralId, blockMeta);
if (blockNumeralId == 0) state = BlockState.AIR; //use the static field to increase render speed (== comparison)
numeralMappings.put(idmeta, state);
} else {
BlockIDMeta idmeta = new BlockIDMeta(blockId, blockMeta);
idMappings.put(idmeta, state);
}
} catch (NumberFormatException ex) {
Logger.global.logWarning("Loading BlockIdConfig: Failed to parse blockid:meta from key '" + key + "'");
} catch (IllegalArgumentException ex) {
@ -79,22 +90,22 @@ public class BlockIdConfig implements BlockIdMapper {
}
@Override
public BlockState get(int id, int meta) {
if (id == 0) return BlockState.AIR;
public BlockState get(int numeralId, int meta) {
if (numeralId == 0) return BlockState.AIR;
BlockIDMeta idmeta = new BlockIDMeta(id, meta);
BlockState state = mappings.get(idmeta);
BlockNumeralIDMeta numidmeta = new BlockNumeralIDMeta(numeralId, meta);
BlockState state = numeralMappings.get(numidmeta);
if (state == null) {
state = mappings.getOrDefault(new BlockIDMeta(id, 0), BlockState.MISSING); //meta-fallback
state = numeralMappings.getOrDefault(new BlockNumeralIDMeta(numeralId, 0), BlockState.MISSING); //meta-fallback
if (autopoulationConfigLoader != null) {
mappings.put(idmeta, state);
numeralMappings.put(numidmeta, state);
synchronized (autopoulationConfigLoader) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.getNode(id + ":" + meta).setValue(state.toString());
node.getNode(numeralId + ":" + meta).setValue(state.toString());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex);
@ -105,12 +116,50 @@ public class BlockIdConfig implements BlockIdMapper {
return state;
}
@Override
public BlockState get(String id, int numeralId, int meta) {
if (numeralId == 0) return BlockState.AIR;
BlockIDMeta idmeta = new BlockIDMeta(id, meta);
BlockState state = idMappings.get(idmeta);
if (state == null) {
BlockNumeralIDMeta numidmeta = new BlockNumeralIDMeta(numeralId, meta);
state = numeralMappings.get(numidmeta);
if (state == null) {
state = idMappings.get(new BlockIDMeta(id, 0));
if (state == null) {
state = numeralMappings.get(new BlockNumeralIDMeta(numeralId, 0));
if (state == null) state = new BlockState(id);
}
if (autopoulationConfigLoader != null) {
idMappings.put(idmeta, state);
numeralMappings.put(numidmeta, state);
synchronized (autopoulationConfigLoader) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.getNode(id + ":" + meta).setValue(state.toString());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex);
}
}
}
}
}
return state;
}
class BlockIDMeta {
class BlockNumeralIDMeta {
private final int id;
private final int meta;
public BlockIDMeta(int id, int meta) {
public BlockNumeralIDMeta(int id, int meta) {
this.id = id;
this.meta = meta;
}
@ -125,13 +174,13 @@ public class BlockIdConfig implements BlockIdMapper {
@Override
public int hashCode() {
return id * 0xFFFF + meta;
return id * 16 + meta;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BlockIDMeta) {
BlockIDMeta other = (BlockIDMeta) obj;
if (obj instanceof BlockNumeralIDMeta) {
BlockNumeralIDMeta other = (BlockNumeralIDMeta) obj;
return other.id == id && other.meta == meta;
}
@ -139,4 +188,37 @@ public class BlockIdConfig implements BlockIdMapper {
}
}
class BlockIDMeta {
private final String id;
private final int meta;
public BlockIDMeta(String id, int meta) {
this.id = id;
this.meta = meta;
}
public String getId() {
return id;
}
public int getMeta() {
return meta;
}
@Override
public int hashCode() {
return id.hashCode() * 16 + meta;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BlockIDMeta) {
BlockIDMeta other = (BlockIDMeta) obj;
return other.id.equals(id) && other.meta == meta;
}
return false;
}
}
}

View File

@ -68,8 +68,8 @@ public class BlockPropertiesConfig implements BlockPropertiesMapper {
try {
BlockState bsKey = BlockState.fromString(key);
BlockProperties bsValue = new BlockProperties(
e.getValue().getNode("culling").getBoolean(false),
e.getValue().getNode("occluding").getBoolean(false),
e.getValue().getNode("culling").getBoolean(true),
e.getValue().getNode("occluding").getBoolean(true),
e.getValue().getNode("flammable").getBoolean(false)
);
BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValue);

View File

@ -154,7 +154,7 @@ public class ConfigManager {
blockIdsConfigNode = joinFromResourcePack(resourcePack, "blockIds.json", blockIdsConfigNode);
blockIdConfig = new BlockIdConfig(
blockIdsConfigNode,
null //getLoader(makeAutogen(getBlockIdConfigFile()))
getLoader(makeAutogen(getBlockIdConfigFile()))
);
//load blockProperties.json from resources, config-folder and resourcepack
@ -170,7 +170,7 @@ public class ConfigManager {
blockPropertiesConfig = new BlockPropertiesConfig(
blockPropertiesConfigNode,
resourcePack,
null //getLoader(makeAutogen(getBlockPropertiesConfigFile()))
getLoader(makeAutogen(getBlockPropertiesConfigFile()))
);
//load biomes.json from resources, config-folder and resourcepack
@ -185,7 +185,7 @@ public class ConfigManager {
biomeConfigNode = joinFromResourcePack(resourcePack, "biomes.json", biomeConfigNode);
biomeConfig = new BiomeConfig(
biomeConfigNode,
null //getLoader(makeAutogen(getBiomeConfigFile()))
getLoader(makeAutogen(getBiomeConfigFile()))
);
}
@ -255,13 +255,11 @@ public class ConfigManager {
return configNode;
}
/*
private File makeAutogen(File file) throws IOException {
File autogenFile = file.getCanonicalFile().toPath().getParent().resolve("generated").resolve(file.getName()).toFile();
File autogenFile = file.getCanonicalFile().toPath().getParent().resolve("missing-configs").resolve(file.getName()).toFile();
autogenFile.getParentFile().mkdirs();
return autogenFile;
}
*/
private ConfigurationLoader<? extends ConfigurationNode> getLoader(String filename, InputStream is){
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));

View File

@ -149,9 +149,12 @@ public class ChunkAnvil112 extends Chunk {
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
BlockState blockState = blockIdMapper.get(blockId, blockData);
return blockState;
String forgeIdMapping = getWorld().getForgeBlockIdMapping(blockId);
if (forgeIdMapping != null) {
return blockIdMapper.get(forgeIdMapping, blockId, blockData);
} else {
return blockIdMapper.get(blockId, blockData);
}
}
public String getBlockIdMeta(Vector3i pos) {
@ -169,8 +172,9 @@ public class ChunkAnvil112 extends Chunk {
}
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
String forgeIdMapping = getWorld().getForgeBlockIdMapping(blockId);
return blockId + ":" + blockData;
return blockId + ":" + blockData + " " + forgeIdMapping;
}
public LightData getLightData(Vector3i pos) {

View File

@ -35,7 +35,9 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@ -71,6 +73,7 @@ import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
import de.bluecolored.bluemap.core.world.World;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.ListTag;
import net.querz.nbt.NBTUtil;
import net.querz.nbt.Tag;
import net.querz.nbt.mca.CompressionType;
@ -105,6 +108,8 @@ public class MCAWorld implements World {
private BlockPropertiesMapper blockPropertiesMapper;
private BiomeMapper biomeMapper;
private Map<Integer, String> forgeBlockMappings;
private MCAWorld(
Path worldFolder,
UUID uuid,
@ -125,8 +130,10 @@ public class MCAWorld implements World {
this.blockIdMapper = blockIdMapper;
this.blockPropertiesMapper = blockPropertiesMapper;
this.biomeMapper = biomeMapper;
this.forgeBlockMappings = new HashMap<>();
}
public BlockState getBlockState(Vector3i pos) {
try {
@ -335,6 +342,10 @@ public class MCAWorld implements World {
public Path getWorldFolder() {
return worldFolder;
}
public String getForgeBlockIdMapping(int id) {
return forgeBlockMappings.get(id);
}
private Path getRegionFolder() {
return worldFolder.resolve("region");
@ -360,7 +371,8 @@ public class MCAWorld implements World {
CHUNK_CACHE.invalidateAll();
return new MCAWorld(
MCAWorld world = new MCAWorld(
worldFolder,
uuid,
name,
@ -371,6 +383,21 @@ public class MCAWorld implements World {
blockPropertiesMapper,
biomeIdMapper
);
try {
ListTag<? extends Tag<?>> blockIdReg = level.getCompoundTag("FML").getCompoundTag("Registries").getCompoundTag("minecraft:blocks").getListTag("ids");
for (Tag<?> tag : blockIdReg) {
if (tag instanceof CompoundTag) {
CompoundTag entry = (CompoundTag) tag;
String blockId = entry.getString("K");
int numeralId = entry.getInt("V");
world.forgeBlockMappings.put(numeralId, blockId);
}
}
} catch (NullPointerException ignore) {}
return world;
} catch (ClassCastException | NullPointerException ex) {
throw new IOException("Invaid level.dat format!", ex);
}

View File

@ -26,9 +26,10 @@ package de.bluecolored.bluemap.core.mca.mapping;
import de.bluecolored.bluemap.core.world.BlockState;
@FunctionalInterface
public interface BlockIdMapper {
BlockState get(int id, int meta);
BlockState get(String id, int numeralId, int meta);
}

View File

@ -86,6 +86,11 @@ public class BlockStateResource {
models.add(variant.getModel(pos));
}
}
//fallback to first variant
if (models.isEmpty() && !variants.isEmpty()) {
models.add(variants.get(0).getModel(pos));
}
return models;
}

View File

@ -45,7 +45,6 @@ public class Commands {
public CommandSpec createRootCommand() {
@SuppressWarnings("unused")
CommandSpec debugCommand = CommandSpec.builder()
.executor((source, args) -> {
if (source instanceof Locatable) {
@ -62,8 +61,8 @@ public class Commands {
try {
Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(loc.getBlockPosition()));
if (chunk instanceof ChunkAnvil112) {
blockIdMeta = " (id:" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition()) + ")";
blockBelowIdMeta = " (id:" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition().add(0, -1, 0)) + ")";
blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition()) + ")";
blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(loc.getBlockPosition().add(0, -1, 0)) + ")";
}
} catch (IOException ex) {
Logger.global.logError("Failed to read chunk for debug!", ex);
@ -71,8 +70,8 @@ public class Commands {
}
source.sendMessages(Lists.newArrayList(
Text.of("Block: " + block + blockIdMeta),
Text.of("Block below: " + blockBelow + blockBelowIdMeta)
Text.of(TextColors.GOLD, "Block at you: ", TextColors.RESET, block, TextColors.GRAY, blockIdMeta),
Text.of(TextColors.GOLD, "Block below you: ", TextColors.RESET, blockBelow, TextColors.GRAY, blockBelowIdMeta)
));
}
@ -88,7 +87,7 @@ public class Commands {
.child(createPauseRenderCommand(), "pause")
.child(createResumeRenderCommand(), "resume")
.child(createRenderCommand(), "render")
//.child(debugCommand, "debug")
.child(debugCommand, "debug")
.executor((source, args) -> {
source.sendMessages(createStatusMessage());
return CommandResult.success();