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 @@
public class BlockIdConfig implements BlockIdMapper { public class BlockIdConfig implements BlockIdMapper {
private ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader; private ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private Map<BlockIDMeta, BlockState> mappings; private Map<BlockNumeralIDMeta, BlockState> numeralMappings;
private Map<BlockIDMeta, BlockState> idMappings;
public BlockIdConfig(ConfigurationNode node) { public BlockIdConfig(ConfigurationNode node) {
this(node, null); this(node, null);
@ -47,29 +48,39 @@ public BlockIdConfig(ConfigurationNode node) {
public BlockIdConfig(ConfigurationNode node, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) { public BlockIdConfig(ConfigurationNode node, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.autopoulationConfigLoader = autopoulationConfigLoader; this.autopoulationConfigLoader = autopoulationConfigLoader;
mappings = new HashMap<>(); numeralMappings = new HashMap<>();
idMappings = new HashMap<>();
for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){ for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){
String key = e.getKey().toString(); String key = e.getKey().toString();
String value = e.getValue().getString(); String value = e.getValue().getString();
try { try {
int splitIndex = key.indexOf(':'); int splitIndex = key.lastIndexOf(':');
int blockId, blockMeta;
if (splitIndex > 0 && splitIndex < key.length() - 1) { if (splitIndex <= 0 || splitIndex >= key.length() - 1) {
blockId = Integer.parseInt(key.substring(0, splitIndex)); Logger.global.logWarning("Loading BlockIdConfig: Failed to parse blockid:meta from key '" + key + "'");
blockMeta = Integer.parseInt(key.substring(splitIndex + 1)); continue;
} else {
blockId = Integer.parseInt(key);
blockMeta = 0;
} }
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) { } catch (NumberFormatException ex) {
Logger.global.logWarning("Loading BlockIdConfig: Failed to parse blockid:meta from key '" + key + "'"); Logger.global.logWarning("Loading BlockIdConfig: Failed to parse blockid:meta from key '" + key + "'");
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
@ -79,22 +90,22 @@ public BlockIdConfig(ConfigurationNode node, ConfigurationLoader<? extends Confi
} }
@Override @Override
public BlockState get(int id, int meta) { public BlockState get(int numeralId, int meta) {
if (id == 0) return BlockState.AIR; if (numeralId == 0) return BlockState.AIR;
BlockIDMeta idmeta = new BlockIDMeta(id, meta); BlockNumeralIDMeta numidmeta = new BlockNumeralIDMeta(numeralId, meta);
BlockState state = mappings.get(idmeta); BlockState state = numeralMappings.get(numidmeta);
if (state == null) { 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) { if (autopoulationConfigLoader != null) {
mappings.put(idmeta, state); numeralMappings.put(numidmeta, state);
synchronized (autopoulationConfigLoader) { synchronized (autopoulationConfigLoader) {
try { try {
ConfigurationNode node = autopoulationConfigLoader.load(); ConfigurationNode node = autopoulationConfigLoader.load();
node.getNode(id + ":" + meta).setValue(state.toString()); node.getNode(numeralId + ":" + meta).setValue(state.toString());
autopoulationConfigLoader.save(node); autopoulationConfigLoader.save(node);
} catch (IOException ex) { } catch (IOException ex) {
Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex); Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex);
@ -105,12 +116,50 @@ public BlockState get(int id, int meta) {
return state; 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 id;
private final int meta; private final int meta;
public BlockIDMeta(int id, int meta) { public BlockNumeralIDMeta(int id, int meta) {
this.id = id; this.id = id;
this.meta = meta; this.meta = meta;
} }
@ -125,13 +174,13 @@ public int getMeta() {
@Override @Override
public int hashCode() { public int hashCode() {
return id * 0xFFFF + meta; return id * 16 + meta;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof BlockIDMeta) { if (obj instanceof BlockNumeralIDMeta) {
BlockIDMeta other = (BlockIDMeta) obj; BlockNumeralIDMeta other = (BlockNumeralIDMeta) obj;
return other.id == id && other.meta == meta; return other.id == id && other.meta == meta;
} }
@ -139,4 +188,37 @@ public boolean equals(Object obj) {
} }
} }
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 BlockPropertiesConfig(ConfigurationNode node, ResourcePack resourcePack,
try { try {
BlockState bsKey = BlockState.fromString(key); BlockState bsKey = BlockState.fromString(key);
BlockProperties bsValue = new BlockProperties( BlockProperties bsValue = new BlockProperties(
e.getValue().getNode("culling").getBoolean(false), e.getValue().getNode("culling").getBoolean(true),
e.getValue().getNode("occluding").getBoolean(false), e.getValue().getNode("occluding").getBoolean(true),
e.getValue().getNode("flammable").getBoolean(false) e.getValue().getNode("flammable").getBoolean(false)
); );
BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValue); BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValue);

View File

@ -154,7 +154,7 @@ public void loadResourceConfigs(ResourcePack resourcePack) throws IOException {
blockIdsConfigNode = joinFromResourcePack(resourcePack, "blockIds.json", blockIdsConfigNode); blockIdsConfigNode = joinFromResourcePack(resourcePack, "blockIds.json", blockIdsConfigNode);
blockIdConfig = new BlockIdConfig( blockIdConfig = new BlockIdConfig(
blockIdsConfigNode, blockIdsConfigNode,
null //getLoader(makeAutogen(getBlockIdConfigFile())) getLoader(makeAutogen(getBlockIdConfigFile()))
); );
//load blockProperties.json from resources, config-folder and resourcepack //load blockProperties.json from resources, config-folder and resourcepack
@ -170,7 +170,7 @@ public void loadResourceConfigs(ResourcePack resourcePack) throws IOException {
blockPropertiesConfig = new BlockPropertiesConfig( blockPropertiesConfig = new BlockPropertiesConfig(
blockPropertiesConfigNode, blockPropertiesConfigNode,
resourcePack, resourcePack,
null //getLoader(makeAutogen(getBlockPropertiesConfigFile())) getLoader(makeAutogen(getBlockPropertiesConfigFile()))
); );
//load biomes.json from resources, config-folder and resourcepack //load biomes.json from resources, config-folder and resourcepack
@ -185,7 +185,7 @@ public void loadResourceConfigs(ResourcePack resourcePack) throws IOException {
biomeConfigNode = joinFromResourcePack(resourcePack, "biomes.json", biomeConfigNode); biomeConfigNode = joinFromResourcePack(resourcePack, "biomes.json", biomeConfigNode);
biomeConfig = new BiomeConfig( biomeConfig = new BiomeConfig(
biomeConfigNode, biomeConfigNode,
null //getLoader(makeAutogen(getBiomeConfigFile())) getLoader(makeAutogen(getBiomeConfigFile()))
); );
} }
@ -255,13 +255,11 @@ private ConfigurationNode loadOrCreate(File configFile, URL defaultConfig, URL d
return configNode; return configNode;
} }
/*
private File makeAutogen(File file) throws IOException { 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(); autogenFile.getParentFile().mkdirs();
return autogenFile; return autogenFile;
} }
*/
private ConfigurationLoader<? extends ConfigurationNode> getLoader(String filename, InputStream is){ private ConfigurationLoader<? extends ConfigurationNode> getLoader(String filename, InputStream is){
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));

View File

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

View File

@ -35,7 +35,9 @@
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -71,6 +73,7 @@
import de.bluecolored.bluemap.core.world.LightData; import de.bluecolored.bluemap.core.world.LightData;
import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.World;
import net.querz.nbt.CompoundTag; import net.querz.nbt.CompoundTag;
import net.querz.nbt.ListTag;
import net.querz.nbt.NBTUtil; import net.querz.nbt.NBTUtil;
import net.querz.nbt.Tag; import net.querz.nbt.Tag;
import net.querz.nbt.mca.CompressionType; import net.querz.nbt.mca.CompressionType;
@ -105,6 +108,8 @@ public class MCAWorld implements World {
private BlockPropertiesMapper blockPropertiesMapper; private BlockPropertiesMapper blockPropertiesMapper;
private BiomeMapper biomeMapper; private BiomeMapper biomeMapper;
private Map<Integer, String> forgeBlockMappings;
private MCAWorld( private MCAWorld(
Path worldFolder, Path worldFolder,
UUID uuid, UUID uuid,
@ -125,8 +130,10 @@ private MCAWorld(
this.blockIdMapper = blockIdMapper; this.blockIdMapper = blockIdMapper;
this.blockPropertiesMapper = blockPropertiesMapper; this.blockPropertiesMapper = blockPropertiesMapper;
this.biomeMapper = biomeMapper; this.biomeMapper = biomeMapper;
this.forgeBlockMappings = new HashMap<>();
} }
public BlockState getBlockState(Vector3i pos) { public BlockState getBlockState(Vector3i pos) {
try { try {
@ -335,6 +342,10 @@ public void setBiomeMapper(BiomeMapper biomeMapper) {
public Path getWorldFolder() { public Path getWorldFolder() {
return worldFolder; return worldFolder;
} }
public String getForgeBlockIdMapping(int id) {
return forgeBlockMappings.get(id);
}
private Path getRegionFolder() { private Path getRegionFolder() {
return worldFolder.resolve("region"); return worldFolder.resolve("region");
@ -360,7 +371,8 @@ public static MCAWorld load(Path worldFolder, UUID uuid, BlockIdMapper blockIdMa
CHUNK_CACHE.invalidateAll(); CHUNK_CACHE.invalidateAll();
return new MCAWorld(
MCAWorld world = new MCAWorld(
worldFolder, worldFolder,
uuid, uuid,
name, name,
@ -371,6 +383,21 @@ public static MCAWorld load(Path worldFolder, UUID uuid, BlockIdMapper blockIdMa
blockPropertiesMapper, blockPropertiesMapper,
biomeIdMapper 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) { } catch (ClassCastException | NullPointerException ex) {
throw new IOException("Invaid level.dat format!", ex); throw new IOException("Invaid level.dat format!", ex);
} }

View File

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

View File

@ -86,6 +86,11 @@ public Collection<TransformedBlockModelResource> getModels(BlockState blockState
models.add(variant.getModel(pos)); models.add(variant.getModel(pos));
} }
} }
//fallback to first variant
if (models.isEmpty() && !variants.isEmpty()) {
models.add(variants.get(0).getModel(pos));
}
return models; return models;
} }

View File

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