BlueMap/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/BlockIdConfig.java

253 lines
7.3 KiB
Java

/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.config;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
import de.bluecolored.bluemap.core.world.BlockState;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.loader.ConfigurationLoader;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class BlockIdConfig implements BlockIdMapper {
private final ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private final Map<BlockNumeralIDMeta, BlockState> numeralMappings;
private final Map<BlockIDMeta, BlockState> idMappings;
private final ReentrantReadWriteLock lock;
public BlockIdConfig(ConfigurationNode node) {
this(node, null);
}
public BlockIdConfig(ConfigurationNode node, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.autopoulationConfigLoader = autopoulationConfigLoader;
this.numeralMappings = new ConcurrentHashMap<>(200, 0.5f, 8);
this.idMappings = new ConcurrentHashMap<>(200, 0.5f, 8);
this.lock = new ReentrantReadWriteLock();
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String key = e.getKey().toString();
String value = e.getValue().getString();
try {
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;
}
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(MinecraftVersion.EARLIEST_SUPPORTED, value);
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) {
Logger.global.logWarning("Loading BlockIdConfig: Failed to parse BlockState from value '" + value + "'");
}
}
}
@Override
public BlockState get(int numeralId, int meta) {
if (numeralId == 0) return BlockState.AIR;
BlockNumeralIDMeta numidmeta;
BlockState state;
try { lock.readLock().lock();
numidmeta = new BlockNumeralIDMeta(numeralId, meta);
state = numeralMappings.get(numidmeta);
} finally { lock.readLock().unlock(); }
if (state == null) {
try { lock.writeLock().lock();
state = numeralMappings.getOrDefault(new BlockNumeralIDMeta(numeralId, 0), BlockState.MISSING); //meta-fallback
numeralMappings.put(numidmeta, state);
if (autopoulationConfigLoader != null) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.node(numeralId + ":" + meta).set(state.toString());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex);
}
}
} finally { lock.writeLock().unlock(); }
}
return state;
}
@Override
public BlockState get(String id, int numeralId, int meta) {
if (numeralId == 0) return BlockState.AIR;
BlockNumeralIDMeta numidmeta;
BlockState state;
BlockIDMeta idmeta = null;
try { lock.readLock().lock();
numidmeta = new BlockNumeralIDMeta(numeralId, meta);
state = numeralMappings.get(numidmeta);
if (state == null) {
idmeta = new BlockIDMeta(id, meta);
state = idMappings.get(idmeta);
}
} finally { lock.readLock().unlock(); }
if (state == null) {
try { lock.writeLock().lock();
state = idMappings.get(new BlockIDMeta(id, 0));
if (state == null) {
state = numeralMappings.get(new BlockNumeralIDMeta(numeralId, 0));
if (state == null) state = new BlockState(MinecraftVersion.EARLIEST_SUPPORTED, id);
}
idMappings.put(idmeta, state);
if (autopoulationConfigLoader != null) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.node(id + ":" + meta).set(state.toString());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex);
}
}
} finally { lock.writeLock().unlock(); }
}
return state;
}
static class BlockNumeralIDMeta {
private final int id;
private final int meta;
public BlockNumeralIDMeta(int id, int meta) {
this.id = id;
this.meta = meta;
}
public int getId() {
return id;
}
public int getMeta() {
return meta;
}
@Override
public int hashCode() {
return id * 16 + meta;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BlockNumeralIDMeta) {
BlockNumeralIDMeta other = (BlockNumeralIDMeta) obj;
return other.id == id && other.meta == meta;
}
return false;
}
}
static 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;
}
}
}