Rewrite of the render-engine and big performance improvements

- changed most immutable vectors with mutable ones
- dropped 1.12.2 support
- improved caching and block/chunk access
- much more smaller tweaks
This commit is contained in:
Blue (Lukas Rieger) 2021-07-25 13:08:40 +02:00
parent 8838bd6f32
commit e178f935aa
No known key found for this signature in database
GPG Key ID: 904C4995F9E1F800
178 changed files with 1317 additions and 26641 deletions

View File

@ -30,7 +30,6 @@ import de.bluecolored.bluemap.common.web.WebSettings;
import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.config.*; import de.bluecolored.bluemap.core.config.*;
import de.bluecolored.bluemap.core.debug.DebugDump; import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.debug.OneBlockWorld;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.map.hires.RenderSettings;
@ -136,9 +135,6 @@ public class BlueMapService {
maps = new HashMap<>(); maps = new HashMap<>();
worlds = new HashMap<>(); worlds = new HashMap<>();
ConfigManager configManager = getConfigManager();
configManager.loadResourceConfigs(configFolder, getResourcePack());
for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) { for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) {
String id = mapConfig.getId(); String id = mapConfig.getId();
String name = mapConfig.getName(); String name = mapConfig.getName();
@ -160,7 +156,7 @@ public class BlueMapService {
World world = worlds.get(worldUUID); World world = worlds.get(worldUUID);
if (world == null) { if (world == null) {
try { try {
world = MCAWorld.load(worldFolder.toPath(), worldUUID, minecraftVersion, configManager.getBlockIdConfig(), configManager.getBlockPropertiesConfig(), configManager.getBiomeConfig(), worldNameProvider.apply(worldUUID), mapConfig.isIgnoreMissingLightData()); world = MCAWorld.load(worldFolder.toPath(), worldUUID, worldNameProvider.apply(worldUUID), mapConfig.isIgnoreMissingLightData());
worlds.put(worldUUID, world); worlds.put(worldUUID, world);
} catch (MissingResourcesException e) { } catch (MissingResourcesException e) {
throw e; // rethrow this to stop loading and display resource-missing message throw e; // rethrow this to stop loading and display resource-missing message
@ -230,7 +226,10 @@ public class BlueMapService {
if (resourceExtensionsFile.exists()) FileUtils.forceDelete(resourceExtensionsFile); if (resourceExtensionsFile.exists()) FileUtils.forceDelete(resourceExtensionsFile);
FileUtils.forceMkdirParent(resourceExtensionsFile); FileUtils.forceMkdirParent(resourceExtensionsFile);
FileUtils.copyURLToFile(Plugin.class.getResource("/de/bluecolored/bluemap/" + minecraftVersion.getResource().getResourcePrefix() + "/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000); URL resourceExtensionsUrl = Objects.requireNonNull(
Plugin.class.getResource("/de/bluecolored/bluemap/" + minecraftVersion.getResource().getResourcePrefix() + "/resourceExtensions.zip")
);
FileUtils.copyURLToFile(resourceExtensionsUrl, resourceExtensionsFile, 10000, 10000);
//find more resource packs //find more resource packs
File[] resourcePacks = resourcePackFolder.listFiles(); File[] resourcePacks = resourcePackFolder.listFiles();
@ -243,7 +242,7 @@ public class BlueMapService {
resources.add(resourceExtensionsFile); resources.add(resourceExtensionsFile);
try { try {
resourcePack = new ResourcePack(minecraftVersion); resourcePack = new ResourcePack();
if (textureExportFile.exists()) resourcePack.loadTextureFile(textureExportFile); if (textureExportFile.exists()) resourcePack.loadTextureFile(textureExportFile);
resourcePack.load(resources); resourcePack.load(resources);
resourcePack.saveTextureFile(textureExportFile); resourcePack.saveTextureFile(textureExportFile);

View File

@ -59,10 +59,8 @@ import de.bluecolored.bluemap.core.debug.StateDumper;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.map.MapRenderState; import de.bluecolored.bluemap.core.map.MapRenderState;
import de.bluecolored.bluemap.core.mca.ChunkAnvil112;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.World;
import java.io.IOException; import java.io.IOException;
@ -528,24 +526,21 @@ public class Commands<S> {
new Thread(() -> { new Thread(() -> {
// collect and output debug info // collect and output debug info
Vector3i blockPos = position.floor().toInt(); Vector3i blockPos = position.floor().toInt();
Block block = world.getBlock(blockPos); Block<?> block = new Block<>(world, blockPos.getX(), blockPos.getY(), blockPos.getZ());
Block blockBelow = world.getBlock(blockPos.add(0, -1, 0)); Block<?> blockBelow = new Block<>(null, 0, 0, 0).copy(block, 0, -1, 0);
String blockIdMeta = ""; // populate lazy-loaded values
String blockBelowIdMeta = ""; block.getBlockState();
block.getBiomeId();
block.getLightData();
Vector2i chunkPos = world.getChunkGrid().getCell(blockPos.toVector2(true)); blockBelow.getBlockState();
Chunk chunk = world.getChunk(chunkPos.getX(), chunkPos.getY()); blockBelow.getBiomeId();
blockBelow.getLightData();
if (chunk instanceof ChunkAnvil112) {
blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos) + ")";
blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos.add(0, -1, 0)) + ")";
}
source.sendMessages(Arrays.asList( source.sendMessages(Arrays.asList(
Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta), Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block),
Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta), Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow)
Text.of(TextColor.GOLD, "Chunk: ", TextColor.WHITE, chunk)
)); ));
}).start(); }).start();

View File

@ -1,85 +0,0 @@
/*
* 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.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome;
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;
public class BiomeConfig implements BiomeMapper {
private final ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private final Map<Integer, Biome> biomes;
public BiomeConfig(ConfigurationNode node) {
this(node, null);
}
public BiomeConfig(ConfigurationNode node, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.autopoulationConfigLoader = autopoulationConfigLoader;
biomes = new ConcurrentHashMap<>(200, 0.5f, 8);
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String id = e.getKey().toString();
Biome biome = Biome.create(id, e.getValue());
biomes.put(biome.getNumeralId(), biome);
}
}
@Override
public Biome get(int id) {
Biome biome = biomes.get(id);
if (biome == null) {
if (autopoulationConfigLoader != null) {
biomes.put(id, Biome.DEFAULT);
synchronized (autopoulationConfigLoader) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.node("unknown:" + id).node("id").set(id);
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("biomeconf-autopopulate-ioex", "Failed to auto-populate BiomeConfig!", ex);
}
}
}
return Biome.DEFAULT;
}
return biome;
}
}

View File

@ -1,252 +0,0 @@
/*
* 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;
}
}
}

View File

@ -1,141 +0,0 @@
/*
* 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 com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.world.BlockProperties;
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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
public class BlockPropertiesConfig implements BlockPropertiesMapper {
private final ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private final Map<String, List<BlockStateMapping<BlockProperties>>> mappings;
private final LoadingCache<BlockState, BlockProperties> mappingCache;
private final ResourcePack resourcePack;
public BlockPropertiesConfig(ConfigurationNode node, ResourcePack resourcePack) {
this(node, resourcePack, null);
}
public BlockPropertiesConfig(ConfigurationNode node, ResourcePack resourcePack, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.resourcePack = resourcePack;
this.autopoulationConfigLoader = autopoulationConfigLoader;
mappings = new ConcurrentHashMap<>();
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String key = e.getKey().toString();
try {
BlockState bsKey = BlockState.fromString(resourcePack.getMinecraftVersion(), key);
BlockProperties bsValue = new BlockProperties(
e.getValue().node("culling").getBoolean(true),
e.getValue().node("occluding").getBoolean(true),
e.getValue().node("flammable").getBoolean(false)
);
BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValue);
mappings.computeIfAbsent(bsKey.getFullId(), k -> new ArrayList<>()).add(mapping);
} catch (IllegalArgumentException ex) {
Logger.global.logWarning("Loading BlockPropertiesConfig: Failed to parse BlockState from key '" + key + "'");
}
}
mappingCache = Caffeine.newBuilder()
.executor(BlueMap.THREAD_POOL)
.maximumSize(10000)
.build(this::mapNoCache);
}
@Override
public BlockProperties get(BlockState from){
return mappingCache.get(from);
}
private BlockProperties mapNoCache(BlockState bs){
for (BlockStateMapping<BlockProperties> bm : mappings.getOrDefault(bs.getFullId(), Collections.emptyList())){
if (bm.fitsTo(bs)){
return bm.getMapping();
}
}
BlockProperties generated = BlockProperties.SOLID;
MinecraftVersion version = MinecraftVersion.LATEST_SUPPORTED;
if (resourcePack != null) {
try {
boolean culling = false;
boolean occluding = false;
for(TransformedBlockModelResource model : resourcePack.getBlockStateResource(bs).getModels(bs, new ArrayList<>(10))) {
culling = culling || model.getModel().isCulling();
occluding = occluding || model.getModel().isOccluding();
if (culling && occluding) break;
}
generated = new BlockProperties(culling, occluding, generated.isFlammable());
} catch (NoSuchResourceException ignore) {} //ignoring this because it will be logged later again if we try to render that block
version = resourcePack.getMinecraftVersion();
}
mappings.computeIfAbsent(bs.getFullId(), k -> new ArrayList<>()).add(new BlockStateMapping<>(new BlockState(version, bs.getFullId()), generated));
if (autopoulationConfigLoader != null) {
synchronized (autopoulationConfigLoader) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
ConfigurationNode bpNode = node.node(bs.getFullId());
bpNode.node("culling").set(generated.isCulling());
bpNode.node("occluding").set(generated.isOccluding());
bpNode.node("flammable").set(generated.isFlammable());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockpropsconf-autopopulate-ioex", "Failed to auto-populate BlockPropertiesConfig!", ex);
}
}
}
return generated;
}
}

View File

@ -26,16 +26,16 @@ package de.bluecolored.bluemap.core.config;
import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack.Resource;
import de.bluecolored.bluemap.core.util.FileUtils; import de.bluecolored.bluemap.core.util.FileUtils;
import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.gson.GsonConfigurationLoader; import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.spongepowered.configurate.hocon.HoconConfigurationLoader; import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
import org.spongepowered.configurate.loader.ConfigurationLoader; import org.spongepowered.configurate.loader.ConfigurationLoader;
import java.io.*; import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@ -53,11 +53,7 @@ public class ConfigManager {
CONFIG_PLACEHOLDERS.add(new Placeholder("version", BlueMap.VERSION)); CONFIG_PLACEHOLDERS.add(new Placeholder("version", BlueMap.VERSION));
CONFIG_PLACEHOLDERS.add(new Placeholder("datetime-iso", () -> LocalDateTime.now().withNano(0).toString())); CONFIG_PLACEHOLDERS.add(new Placeholder("datetime-iso", () -> LocalDateTime.now().withNano(0).toString()));
} }
private BlockIdConfig blockIdConfig;
private BlockPropertiesConfig blockPropertiesConfig;
private BiomeConfig biomeConfig;
/** /**
* Loads or creates a config file for BlueMap. * Loads or creates a config file for BlueMap.
* *
@ -66,8 +62,8 @@ public class ConfigManager {
* @param defaultValues The default values used if a key is not present in the config (can be null) * @param defaultValues The default values used if a key is not present in the config (can be null)
* @param usePlaceholders Whether to replace placeholders from the defaultConfig if it is newly generated * @param usePlaceholders Whether to replace placeholders from the defaultConfig if it is newly generated
* @param generateEmptyConfig Whether to generate an empty config file if no default config is provided * @param generateEmptyConfig Whether to generate an empty config file if no default config is provided
* @return * @return The loaded configuration node
* @throws IOException * @throws IOException if an IOException occurs while loading
*/ */
public ConfigurationNode loadOrCreate(File configFile, URL defaultConfig, URL defaultValues, boolean usePlaceholders, boolean generateEmptyConfig) throws IOException { public ConfigurationNode loadOrCreate(File configFile, URL defaultConfig, URL defaultValues, boolean usePlaceholders, boolean generateEmptyConfig) throws IOException {
@ -116,107 +112,6 @@ public class ConfigManager {
return configNode; return configNode;
} }
public void loadResourceConfigs(File configFolder, ResourcePack resourcePack) throws IOException {
//load blockColors.json from resources, config-folder and resourcepack
URL blockColorsConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/blockColors.json");
File blockColorsConfigFile = new File(configFolder, "blockColors.json");
ConfigurationNode blockColorsConfigNode = loadOrCreate(
blockColorsConfigFile,
null,
blockColorsConfigUrl,
false,
false
);
blockColorsConfigNode = joinFromResourcePack(resourcePack, "blockColors.json", blockColorsConfigNode);
resourcePack.getBlockColorCalculatorFactory().loadColorConfig(blockColorsConfigNode);
//load blockIds.json from resources, config-folder and resourcepack
URL blockIdsConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/blockIds.json");
File blockIdsConfigFile = new File(configFolder, "blockIds.json");
ConfigurationNode blockIdsConfigNode = loadOrCreate(
blockIdsConfigFile,
null,
blockIdsConfigUrl,
false,
false
);
blockIdsConfigNode = joinFromResourcePack(resourcePack, "blockIds.json", blockIdsConfigNode);
blockIdConfig = new BlockIdConfig(
blockIdsConfigNode
);
//load blockProperties.json from resources, config-folder and resourcepack
URL blockPropertiesConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/blockProperties.json");
File blockPropertiesConfigFile = new File(configFolder, "blockProperties.json");
ConfigurationNode blockPropertiesConfigNode = loadOrCreate(
blockPropertiesConfigFile,
null,
blockPropertiesConfigUrl,
false,
false
);
blockPropertiesConfigNode = joinFromResourcePack(resourcePack, "blockProperties.json", blockPropertiesConfigNode);
blockPropertiesConfig = new BlockPropertiesConfig(
blockPropertiesConfigNode,
resourcePack
);
//load biomes.json from resources, config-folder and resourcepack
URL biomeConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/biomes.json");
File biomeConfigFile = new File(configFolder, "biomes.json");
ConfigurationNode biomeConfigNode = loadOrCreate(
biomeConfigFile,
null,
biomeConfigUrl,
false,
false
);
biomeConfigNode = joinFromResourcePack(resourcePack, "biomes.json", biomeConfigNode);
biomeConfig = new BiomeConfig(
biomeConfigNode
);
}
public BlockIdConfig getBlockIdConfig() {
return blockIdConfig;
}
public BlockPropertiesConfig getBlockPropertiesConfig() {
return blockPropertiesConfig;
}
public BiomeConfig getBiomeConfig() {
return biomeConfig;
}
private ConfigurationNode joinFromResourcePack(ResourcePack resourcePack, String configFileName, ConfigurationNode defaultConfig) {
ConfigurationNode joinedNode = null;
for (Resource resource : resourcePack.getConfigAdditions(configFileName)) {
try {
ConfigurationNode node = getLoader(configFileName, resource.read()).load();
if (joinedNode == null) joinedNode = node;
else joinedNode.mergeFrom(node);
} catch (IOException ex) {
Logger.global.logWarning("Failed to load an additional " + configFileName + " from the resource-pack! " + ex);
}
}
if (joinedNode == null) return defaultConfig;
joinedNode.mergeFrom(defaultConfig);
return joinedNode;
}
private ConfigurationLoader<? extends ConfigurationNode> getLoader(String filename, InputStream is){
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
if (filename.endsWith(".json")) return GsonConfigurationLoader.builder().source(() -> reader).build();
else return HoconConfigurationLoader.builder().source(() -> reader).build();
}
private ConfigurationLoader<? extends ConfigurationNode> getLoader(URL url){ private ConfigurationLoader<? extends ConfigurationNode> getLoader(URL url){
if (url.getFile().endsWith(".json")) return GsonConfigurationLoader.builder().url(url).build(); if (url.getFile().endsWith(".json")) return GsonConfigurationLoader.builder().url(url).build();
else return HoconConfigurationLoader.builder().url(url).build(); else return HoconConfigurationLoader.builder().url(url).build();

View File

@ -1,9 +1,35 @@
/*
* 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.debug; package de.bluecolored.bluemap.core.debug;
import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper; import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.*; import de.bluecolored.bluemap.core.world.Grid;
import de.bluecolored.bluemap.core.world.Region;
import de.bluecolored.bluemap.core.world.World;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
@ -62,22 +88,6 @@ public class OneBlockWorld implements World {
return delegate.getRegionGrid(); return delegate.getRegionGrid();
} }
@Override
public Biome getBiome(int x, int y, int z) {
return Biome.DEFAULT;
}
@Override
public BlockState getBlockState(int x, int y, int z) {
if (x == 0 && z == 0 && y == 70) return BlockState.MISSING;
return BlockState.AIR;
}
@Override
public BlockProperties getBlockProperties(BlockState blockState) {
return delegate.getBlockProperties(blockState);
}
@Override @Override
public Chunk getChunkAtBlock(int x, int y, int z) { public Chunk getChunkAtBlock(int x, int y, int z) {
return delegate.getChunkAtBlock(x, y, z); return delegate.getChunkAtBlock(x, y, z);
@ -113,9 +123,4 @@ public class OneBlockWorld implements World {
delegate.cleanUpChunkCache(); delegate.cleanUpChunkCache();
} }
@Override
public BlockPropertiesMapper getBlockPropertiesMapper() {
return delegate.getBlockPropertiesMapper();
}
} }

View File

@ -1,3 +1,27 @@
/*
* 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.map.hires; package de.bluecolored.bluemap.core.map.hires;
import de.bluecolored.bluemap.core.util.math.MatrixM3f; import de.bluecolored.bluemap.core.util.math.MatrixM3f;

View File

@ -29,7 +29,7 @@ import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModelFactory;
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException; import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.World;
@ -54,9 +54,8 @@ public class HiresModelRenderer {
BlockStateModelFactory modelFactory = new BlockStateModelFactory(resourcePack, renderSettings); BlockStateModelFactory modelFactory = new BlockStateModelFactory(resourcePack, renderSettings);
int maxHeight, minY, maxY; int maxHeight, minY, maxY;
float dx, dz;
Color columnColor = new Color(), blockColor = new Color(); Color columnColor = new Color(), blockColor = new Color();
Block block = new Block(world, 0, 0, 0); BlockNeighborhood<?> block = new BlockNeighborhood<>(resourcePack, world, 0, 0, 0);
BlockModelView blockModel = new BlockModelView(model); BlockModelView blockModel = new BlockModelView(model);
int x, y, z; int x, y, z;
@ -96,13 +95,6 @@ public class HiresModelRenderer {
maxHeight = y; maxHeight = y;
columnColor.overlay(blockColor); columnColor.overlay(blockColor);
} }
//random offset
if (block.getBlockState().isRandomOffset){
dx = (hashToFloat(x, z, 123984) - 0.5f) * 0.75f;
dz = (hashToFloat(x, z, 345542) - 0.5f) * 0.75f;
blockModel.translate(dx, 0, dz);
}
} }
tileMeta.setHeight(x, z, maxHeight); tileMeta.setHeight(x, z, maxHeight);
@ -113,20 +105,4 @@ public class HiresModelRenderer {
return tileMeta; return tileMeta;
} }
/**
* Hashes the provided position to a random float between 0 and 1.<br>
* <br>
* <i>(Implementation adapted from https://github.com/SpongePowered/SpongeAPI/blob/ecd761a70219e467dea47a09fc310e8238e9911f/src/main/java/org/spongepowered/api/extra/skylands/SkylandsUtil.java)</i>
*
* @param x The x component of the position
* @param z The z component of the position
* @param seed A seed for the hashing
* @return The hashed value between 0 and 1
*/
public static float hashToFloat(int x, int z, long seed) {
final long hash = x * 73428767 ^ z * 4382893 ^ seed * 457;
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
}
} }

View File

@ -1,3 +1,27 @@
/*
* 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.map.hires; package de.bluecolored.bluemap.core.map.hires;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;
@ -37,7 +61,7 @@ public class HiresTileMeta {
} }
private void setColor(int x, int z, float r, float g, float b, float a) { private void setColor(int x, int z, float r, float g, float b, float a) {
int index = (x - minX) * sizeZ + (z - minZ) * 4; int index = ((x - minX) * sizeZ + (z - minZ)) * 4;
colors[index ] = r; colors[index ] = r;
colors[index + 1] = g; colors[index + 1] = g;
colors[index + 2] = b; colors[index + 2] = b;
@ -45,7 +69,7 @@ public class HiresTileMeta {
} }
public Color getColor(int x, int z, Color target) { public Color getColor(int x, int z, Color target) {
int index = (x - minX) * sizeZ + (z - minZ) * 4; int index = ((x - minX) * sizeZ + (z - minZ)) * 4;
return target.set( return target.set(
colors[index ], colors[index ],
colors[index + 1], colors[index + 1],

View File

@ -24,12 +24,14 @@
*/ */
package de.bluecolored.bluemap.core.map.hires.blockmodel; package de.bluecolored.bluemap.core.map.hires.blockmodel;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.map.hires.BlockModelView; import de.bluecolored.bluemap.core.map.hires.BlockModelView;
import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.*; import de.bluecolored.bluemap.core.resourcepack.blockstate.BlockStateResource;
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import java.util.ArrayList; import java.util.ArrayList;
@ -46,25 +48,20 @@ public class BlockStateModelFactory {
public BlockStateModelFactory(ResourcePack resourcePack, RenderSettings renderSettings) { public BlockStateModelFactory(ResourcePack resourcePack, RenderSettings renderSettings) {
this.resourcePack = resourcePack; this.resourcePack = resourcePack;
Block[] neighborCache = new Block[3 * 3 * 3]; this.resourceModelBuilder = new ResourceModelBuilder(resourcePack, renderSettings);
for (int i = 0; i < neighborCache.length; i++) { this.liquidModelBuilder = new LiquidModelBuilder(resourcePack, renderSettings);
neighborCache[i] = new Block(null, 0, 0, 0);
}
this.resourceModelBuilder = new ResourceModelBuilder(resourcePack, renderSettings, neighborCache);
this.liquidModelBuilder = new LiquidModelBuilder(resourcePack, renderSettings, neighborCache);
this.bmrs = new ArrayList<>(); this.bmrs = new ArrayList<>();
} }
public void render(Block block, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException { public void render(BlockNeighborhood<?> block, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
render(block, block.getBlockState(), blockModel, blockColor); render(block, block.getBlockState(), blockModel, blockColor);
} }
public void render(Block block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException { public void render(BlockNeighborhood<?> block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
//shortcut for air //shortcut for air
if (blockState.isAir) return; if (blockState.isAir()) return;
int modelStart = blockModel.getStart(); int modelStart = blockModel.getStart();
@ -72,15 +69,14 @@ public class BlockStateModelFactory {
renderModel(block, blockState, blockModel.initialize(), blockColor); renderModel(block, blockState, blockModel.initialize(), blockColor);
// add water if block is waterlogged // add water if block is waterlogged
if (blockState.isWaterlogged) { if (blockState.isWaterlogged() || block.getProperties().isAlwaysWaterlogged()) {
renderModel(block, WATERLOGGED_BLOCKSTATE, blockModel.initialize(), blockColor); renderModel(block, WATERLOGGED_BLOCKSTATE, blockModel.initialize(), blockColor);
} }
blockModel.initialize(modelStart); blockModel.initialize(modelStart);
} }
private void renderModel(Block block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException { private void renderModel(BlockNeighborhood<?> block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
int modelStart = blockModel.getStart(); int modelStart = blockModel.getStart();
BlockStateResource resource = resourcePack.getBlockStateResource(blockState); BlockStateResource resource = resourcePack.getBlockStateResource(blockState);
@ -97,7 +93,7 @@ public class BlockStateModelFactory {
blockModel.initialize(modelStart); blockModel.initialize(modelStart);
} }
private final static BlockState WATERLOGGED_BLOCKSTATE = new BlockState(MinecraftVersion.LATEST_SUPPORTED, "minecraft:water"); private final static BlockState WATERLOGGED_BLOCKSTATE = new BlockState("minecraft:water");
} }

View File

@ -26,22 +26,21 @@ package de.bluecolored.bluemap.core.map.hires.blockmodel;
import com.flowpowered.math.TrigMath; import com.flowpowered.math.TrigMath;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.hires.BlockModelView; import de.bluecolored.bluemap.core.map.hires.BlockModelView;
import de.bluecolored.bluemap.core.map.hires.HiresTileModel; import de.bluecolored.bluemap.core.map.hires.HiresTileModel;
import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculatorFactory; import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculatorFactory;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.Texture; import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.TransformedBlockModelResource; import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.util.Direction; import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.util.math.MatrixM3f; import de.bluecolored.bluemap.core.util.math.MatrixM3f;
import de.bluecolored.bluemap.core.util.math.VectorM2f; import de.bluecolored.bluemap.core.util.math.VectorM2f;
import de.bluecolored.bluemap.core.util.math.VectorM3f; import de.bluecolored.bluemap.core.util.math.VectorM3f;
import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.ResourcePackBlock;
/** /**
* A model builder for all liquid blocks * A model builder for all liquid blocks
@ -57,24 +56,19 @@ public class LiquidModelBuilder {
private final BlockColorCalculatorFactory.BlockColorCalculator blockColorCalculator; private final BlockColorCalculatorFactory.BlockColorCalculator blockColorCalculator;
private final RenderSettings renderSettings; private final RenderSettings renderSettings;
private final boolean useWaterColorMap;
private final VectorM3f[] corners; private final VectorM3f[] corners;
private final Block[] blocksAround;
private final VectorM2f[] uvs = new VectorM2f[4]; private final VectorM2f[] uvs = new VectorM2f[4];
private Block block; private BlockNeighborhood<?> block;
private BlockState blockState; private BlockState blockState;
private TransformedBlockModelResource blockModelResource; private TransformedBlockModelResource blockModelResource;
private BlockModelView blockModel; private BlockModelView blockModel;
private Color blockColor; private Color blockColor;
public LiquidModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings, Block[] neighborCache) { public LiquidModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings) {
this.blockColorCalculator = resourcePack.getBlockColorCalculatorFactory().createCalculator(); this.blockColorCalculator = resourcePack.getBlockColorCalculatorFactory().createCalculator();
this.renderSettings = renderSettings; this.renderSettings = renderSettings;
this.useWaterColorMap = resourcePack.getMinecraftVersion().isAtLeast(new MinecraftVersion(1, 13));
corners = new VectorM3f[]{ corners = new VectorM3f[]{
new VectorM3f( 0, 0, 0 ), new VectorM3f( 0, 0, 0 ),
new VectorM3f( 0, 0, 16 ), new VectorM3f( 0, 0, 16 ),
@ -86,12 +80,10 @@ public class LiquidModelBuilder {
new VectorM3f( 16, 16, 16 ), new VectorM3f( 16, 16, 16 ),
}; };
this.blocksAround = neighborCache;
for (int i = 0; i < uvs.length; i++) uvs[i] = new VectorM2f(0, 0); for (int i = 0; i < uvs.length; i++) uvs[i] = new VectorM2f(0, 0);
} }
public void build(Block block, BlockState blockState, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) { public void build(BlockNeighborhood<?> block, BlockState blockState, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) {
this.block = block; this.block = block;
this.blockState = blockState; this.blockState = blockState;
this.blockModelResource = bmr; this.blockModelResource = bmr;
@ -107,7 +99,7 @@ public class LiquidModelBuilder {
int level = getLiquidLevel(blockState); int level = getLiquidLevel(blockState);
if (level < 8 && !(level == 0 && isSameLiquid(getNeighborBlock(0, 1, 0).getBlockState()))){ if (level < 8 && !(level == 0 && isSameLiquid(block.getNeighborBlock(0, 1, 0)))){
corners[4].y = getLiquidCornerHeight(-1, -1); corners[4].y = getLiquidCornerHeight(-1, -1);
corners[5].y = getLiquidCornerHeight(-1, 0); corners[5].y = getLiquidCornerHeight(-1, 0);
corners[6].y = getLiquidCornerHeight(0, -1); corners[6].y = getLiquidCornerHeight(0, -1);
@ -126,7 +118,7 @@ public class LiquidModelBuilder {
int flowTextureId = flowTexture.getId(); int flowTextureId = flowTexture.getId();
tintcolor.set(1f, 1f, 1f, 1f, true); tintcolor.set(1f, 1f, 1f, 1f, true);
if (useWaterColorMap && blockState.isWater) { if (blockState.isWater()) {
blockColorCalculator.getWaterAverageColor(block, tintcolor); blockColorCalculator.getWaterAverageColor(block, tintcolor);
} }
@ -166,7 +158,7 @@ public class LiquidModelBuilder {
for (ix = x; ix <= x+1; ix++){ for (ix = x; ix <= x+1; ix++){
for (iz = z; iz<= z+1; iz++){ for (iz = z; iz<= z+1; iz++){
if (isSameLiquid(getNeighborBlock(ix, 1, iz).getBlockState())){ if (isSameLiquid(block.getNeighborBlock(ix, 1, iz))){
return 16f; return 16f;
} }
} }
@ -174,12 +166,14 @@ public class LiquidModelBuilder {
float sumHeight = 0f; float sumHeight = 0f;
int count = 0; int count = 0;
ResourcePackBlock<?> neighbor;
BlockState neighborBlockState; BlockState neighborBlockState;
for (ix = x; ix <= x+1; ix++){ for (ix = x; ix <= x+1; ix++){
for (iz = z; iz<= z+1; iz++){ for (iz = z; iz<= z+1; iz++){
neighborBlockState = getNeighborBlock(ix, 0, iz).getBlockState(); neighbor = block.getNeighborBlock(ix, 0, iz);
if (isSameLiquid(neighborBlockState)){ neighborBlockState = neighbor.getBlockState();
if (isSameLiquid(neighbor)){
if (getLiquidLevel(neighborBlockState) == 0) return 14f; if (getLiquidLevel(neighborBlockState) == 0) return 14f;
sumHeight += getLiquidBaseHeight(neighborBlockState); sumHeight += getLiquidBaseHeight(neighborBlockState);
@ -203,9 +197,9 @@ public class LiquidModelBuilder {
return !blockState.equals(BlockState.AIR); return !blockState.equals(BlockState.AIR);
} }
private boolean isSameLiquid(BlockState blockState){ private boolean isSameLiquid(ResourcePackBlock<?> block){
if (blockState.getFullId().equals(this.blockState.getFullId())) return true; if (block.getBlockState().getFullId().equals(this.blockState.getFullId())) return true;
return this.blockState.isWater && blockState.isWaterlogged; return this.blockState.isWater() && (block.getBlockState().isWaterlogged() || block.getProperties().isAlwaysWaterlogged());
} }
private float getLiquidBaseHeight(BlockState block){ private float getLiquidBaseHeight(BlockState block){
@ -223,13 +217,13 @@ public class LiquidModelBuilder {
Vector3i faceDirVector = faceDir.toVector(); Vector3i faceDirVector = faceDir.toVector();
//face culling //face culling
Block bl = getNeighborBlock( ResourcePackBlock<?> bl = block.getNeighborBlock(
faceDirVector.getX(), faceDirVector.getX(),
faceDirVector.getY(), faceDirVector.getY(),
faceDirVector.getZ() faceDirVector.getZ()
); );
if (isSameLiquid(bl.getBlockState()) || (faceDir != Direction.UP && bl.isCullingNeighborFaces())) return false; if (isSameLiquid(bl) || (faceDir != Direction.UP && bl.getProperties().isCulling())) return false;
// initialize the faces // initialize the faces
blockModel.initialize(); blockModel.initialize();
@ -327,17 +321,6 @@ public class LiquidModelBuilder {
return true; return true;
} }
private Block getNeighborBlock(int dx, int dy, int dz) {
int i = (dx + 1) * 9 + (dy + 1) * 3 + (dz + 1);
if (i == 13) return block;
return blocksAround[i].set(
block.getWorld(),
block.getX() + dx,
block.getY() + dy,
block.getZ() + dz
);
}
private final VectorM2f flowingVector = new VectorM2f(0, 0); private final VectorM2f flowingVector = new VectorM2f(0, 0);
private int getFlowingAngle() { private int getFlowingAngle() {
float own = getLiquidBaseHeight(blockState) * BLOCK_SCALE; float own = getLiquidBaseHeight(blockState) * BLOCK_SCALE;
@ -358,11 +341,11 @@ public class LiquidModelBuilder {
} }
private float compareLiquidHeights(float ownHeight, int dx, int dz) { private float compareLiquidHeights(float ownHeight, int dx, int dz) {
BlockState state = getNeighborBlock(dx, 0, dz).getBlockState(); ResourcePackBlock<?> neighbor = block.getNeighborBlock(dx, 0, dz);
if (state.isAir) return 0; if (neighbor.getBlockState().isAir()) return 0;
if (!isSameLiquid(state)) return 0; if (!isSameLiquid(neighbor)) return 0;
float otherHeight = getLiquidBaseHeight(state) * BLOCK_SCALE; float otherHeight = getLiquidBaseHeight(neighbor.getBlockState()) * BLOCK_SCALE;
return otherHeight - ownHeight; return otherHeight - ownHeight;
} }

View File

@ -32,13 +32,19 @@ import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.map.hires.BlockModelView; import de.bluecolored.bluemap.core.map.hires.BlockModelView;
import de.bluecolored.bluemap.core.map.hires.HiresTileModel; import de.bluecolored.bluemap.core.map.hires.HiresTileModel;
import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.*; import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculatorFactory;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.util.Direction; import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.util.math.MatrixM4f; import de.bluecolored.bluemap.core.util.math.MatrixM4f;
import de.bluecolored.bluemap.core.util.math.VectorM2f; import de.bluecolored.bluemap.core.util.math.VectorM2f;
import de.bluecolored.bluemap.core.util.math.VectorM3f; import de.bluecolored.bluemap.core.util.math.VectorM3f;
import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.LightData;
import de.bluecolored.bluemap.core.world.ResourcePackBlock;
/** /**
* This model builder creates a BlockStateModel using the information from parsed resource-pack json files. * This model builder creates a BlockStateModel using the information from parsed resource-pack json files.
@ -54,24 +60,22 @@ public class ResourceModelBuilder {
private final VectorM2f[] uvs = new VectorM2f[4]; private final VectorM2f[] uvs = new VectorM2f[4];
private final Color tintColor = new Color(); private final Color tintColor = new Color();
private final Color mapColor = new Color(); private final Color mapColor = new Color();
private final Block[] blocksAround;
private Block block; private BlockNeighborhood<?> block;
private TransformedBlockModelResource blockModelResource; private TransformedBlockModelResource blockModelResource;
private BlockModelView blockModel; private BlockModelView blockModel;
private Color blockColor; private Color blockColor;
public ResourceModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings, Block[] neighborCache) { public ResourceModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings) {
this.blockColorCalculator = resourcePack.getBlockColorCalculatorFactory().createCalculator(); this.blockColorCalculator = resourcePack.getBlockColorCalculatorFactory().createCalculator();
this.renderSettings = renderSettings; this.renderSettings = renderSettings;
this.blocksAround = neighborCache;
for (int i = 0; i < corners.length; i++) corners[i] = new VectorM3f(0, 0, 0); for (int i = 0; i < corners.length; i++) corners[i] = new VectorM3f(0, 0, 0);
for (int i = 0; i < uvs.length; i++) rawUvs[i] = new VectorM2f(0, 0); for (int i = 0; i < uvs.length; i++) rawUvs[i] = new VectorM2f(0, 0);
} }
private final MatrixM4f modelTransform = new MatrixM4f(); private final MatrixM4f modelTransform = new MatrixM4f();
public void build(Block block, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) { public void build(BlockNeighborhood<?> block, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) {
this.block = block; this.block = block;
this.blockModel = blockModel; this.blockModel = blockModel;
this.blockColor = color; this.blockColor = color;
@ -97,6 +101,13 @@ public class ResourceModelBuilder {
); );
} }
//random offset
if (block.getProperties().isRandomOffset()){
float dx = (hashToFloat(block.getX(), block.getZ(), 123984) - 0.5f) * 0.75f;
float dz = (hashToFloat(block.getX(), block.getZ(), 345542) - 0.5f) * 0.75f;
blockModel.translate(dx, 0, dz);
}
} }
private final MatrixM4f modelElementTransform = new MatrixM4f(); private final MatrixM4f modelElementTransform = new MatrixM4f();
@ -149,14 +160,17 @@ public class ResourceModelBuilder {
// face culling // face culling
if (face.getCullface() != null) { if (face.getCullface() != null) {
Block b = getRotationRelativeBlock(face.getCullface()); ResourcePackBlock<?> b = getRotationRelativeBlock(face.getCullface());
if (b.isCullingNeighborFaces()) return; if (b.getProperties().isCulling()) return;
} }
// light calculation // light calculation
Block facedBlockNeighbor = getRotationRelativeBlock(faceDir); ResourcePackBlock<?> facedBlockNeighbor = getRotationRelativeBlock(faceDir);
int sunLight = facedBlockNeighbor.getPassedSunLight(); LightData blockLightData = block.getLightData();
int blockLight = facedBlockNeighbor.getPassedBlockLight(); LightData facedLightData = facedBlockNeighbor.getLightData();
int sunLight = Math.max(blockLightData.getSkyLight(), facedLightData.getSkyLight());
int blockLight = Math.max(blockLightData.getBlockLight(), facedLightData.getBlockLight());
// filter out faces that are not sun-lighted // filter out faces that are not sun-lighted
if (sunLight == 0f && renderSettings.isExcludeFacesWithoutSunlight()) return; if (sunLight == 0f && renderSettings.isExcludeFacesWithoutSunlight()) return;
@ -302,23 +316,11 @@ public class ResourceModelBuilder {
} }
} }
private Block getNeighborBlock(int dx, int dy, int dz) { private ResourcePackBlock<?> getRotationRelativeBlock(Direction direction){
int i = (dx + 1) * 9 + (dy + 1) * 3 + (dz + 1);
if (i == 13) return block;
return blocksAround[i].set(
block.getWorld(),
block.getX() + dx,
block.getY() + dy,
block.getZ() + dz
);
}
private Block getRotationRelativeBlock(Direction direction){
return getRotationRelativeBlock(direction.toVector()); return getRotationRelativeBlock(direction.toVector());
} }
private ResourcePackBlock<?> getRotationRelativeBlock(Vector3i direction){
private Block getRotationRelativeBlock(Vector3i direction){
return getRotationRelativeBlock( return getRotationRelativeBlock(
direction.getX(), direction.getX(),
direction.getY(), direction.getY(),
@ -327,11 +329,11 @@ public class ResourceModelBuilder {
} }
private final VectorM3f rotationRelativeBlockDirection = new VectorM3f(0, 0, 0); private final VectorM3f rotationRelativeBlockDirection = new VectorM3f(0, 0, 0);
private Block getRotationRelativeBlock(int dx, int dy, int dz){ private ResourcePackBlock<?> getRotationRelativeBlock(int dx, int dy, int dz){
rotationRelativeBlockDirection.set(dx, dy, dz); rotationRelativeBlockDirection.set(dx, dy, dz);
makeRotationRelative(rotationRelativeBlockDirection); makeRotationRelative(rotationRelativeBlockDirection);
return getNeighborBlock( return block.getNeighborBlock(
Math.round(rotationRelativeBlockDirection.x), Math.round(rotationRelativeBlockDirection.x),
Math.round(rotationRelativeBlockDirection.y), Math.round(rotationRelativeBlockDirection.y),
Math.round(rotationRelativeBlockDirection.z) Math.round(rotationRelativeBlockDirection.z)
@ -369,23 +371,28 @@ public class ResourceModelBuilder {
if (x * dirVec.getX() + y * dirVec.getY() > 0){ if (x * dirVec.getX() + y * dirVec.getY() > 0){
if (getRotationRelativeBlock(x, y, 0).isOccludingNeighborFaces()) occluding++; if (getRotationRelativeBlock(x, y, 0).getProperties().isOccluding()) occluding++;
} }
if (x * dirVec.getX() + z * dirVec.getZ() > 0){ if (x * dirVec.getX() + z * dirVec.getZ() > 0){
if (getRotationRelativeBlock(x, 0, z).isOccludingNeighborFaces()) occluding++; if (getRotationRelativeBlock(x, 0, z).getProperties().isOccluding()) occluding++;
} }
if (y * dirVec.getY() + z * dirVec.getZ() > 0){ if (y * dirVec.getY() + z * dirVec.getZ() > 0){
if (getRotationRelativeBlock(0, y, z).isOccludingNeighborFaces()) occluding++; if (getRotationRelativeBlock(0, y, z).getProperties().isOccluding()) occluding++;
} }
if (x * dirVec.getX() + y * dirVec.getY() + z * dirVec.getZ() > 0){ if (x * dirVec.getX() + y * dirVec.getY() + z * dirVec.getZ() > 0){
if (getRotationRelativeBlock(x, y, z).isOccludingNeighborFaces()) occluding++; if (getRotationRelativeBlock(x, y, z).getProperties().isOccluding()) occluding++;
} }
if (occluding > 3) occluding = 3; if (occluding > 3) occluding = 3;
return Math.max(0f, Math.min(1f - occluding * 0.25f, 1f)); return Math.max(0f, Math.min(1f - occluding * 0.25f, 1f));
} }
private static float hashToFloat(int x, int z, long seed) {
final long hash = x * 73428767 ^ z * 4382893 ^ seed * 457;
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
}
} }

View File

@ -1,233 +0,0 @@
/*
* 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.mca;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.ListTag;
import net.querz.nbt.NumberTag;
import java.util.Arrays;
import java.util.function.IntFunction;
public class ChunkAnvil112 extends MCAChunk {
private final BiomeMapper biomeIdMapper;
private final BlockIdMapper blockIdMapper;
private final IntFunction<String> forgeBlockIdMapper;
private boolean isGenerated;
private boolean hasLight;
private Section[] sections;
private byte[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil112(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper, BlockIdMapper blockIdMapper, IntFunction<String> forgeBlockIdMapper) {
super(chunkTag);
this.blockIdMapper = blockIdMapper;
this.biomeIdMapper = biomeIdMapper;
this.forgeBlockIdMapper = forgeBlockIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level");
hasLight = levelData.getBoolean("LightPopulated");
isGenerated =
(hasLight || ignoreMissingLightData) &&
levelData.getBoolean("TerrainPopulated");
sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe?
if (levelData.containsKey("Sections")) {
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
Section section = new Section(sectionTag);
if (section.getSectionY() >= 0 && section.getSectionY() < sections.length) sections[section.getSectionY()] = section;
}
}
biomes = levelData.getByteArray("Biomes");
if (biomes == null || biomes.length == 0) {
biomes = new byte[256];
}
if (biomes.length < 256) {
biomes = Arrays.copyOf(biomes, 256);
}
}
@Override
public boolean isGenerated() {
return isGenerated;
}
@Override
public BlockState getBlockState(int x, int y, int z) {
int sectionY = y >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return BlockState.AIR;
Section section = this.sections[sectionY];
if (section == null) return BlockState.AIR;
return section.getBlockState(x, y, z);
}
public String getBlockIdMeta(Vector3i pos) {
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return "0:0";
Section section = this.sections[sectionY];
if (section == null) return "0:0";
return section.getBlockIdMeta(pos);
}
@Override
public LightData getLightData(int x, int y, int z, LightData target) {
if (!hasLight) return target.set(15, 0);
int sectionY = y >> 4;
if (sectionY < 0 || sectionY >= this.sections.length)
return (y < 0) ? target.set(0, 0) : target.set(15, 0);
Section section = this.sections[sectionY];
if (section == null) return target.set(15, 0);
return section.getLightData(x, y, z, target);
}
@Override
public Biome getBiome(int x, int y, int z) {
x = x & 0xF; // Math.floorMod(pos.getX(), 16)
z = z & 0xF;
int biomeByteIndex = z * 16 + x;
if (biomeByteIndex >= this.biomes.length) return Biome.DEFAULT;
return biomeIdMapper.get(biomes[biomeByteIndex] & 0xFF);
}
private class Section {
private int sectionY;
private byte[] blocks;
private byte[] add;
private byte[] blockLight;
private byte[] skyLight;
private byte[] data;
public Section(CompoundTag sectionData) {
this.sectionY = sectionData.get("Y", NumberTag.class).asInt();
this.blocks = sectionData.getByteArray("Blocks");
this.add = sectionData.getByteArray("Add");
this.blockLight = sectionData.getByteArray("BlockLight");
this.skyLight = sectionData.getByteArray("SkyLight");
this.data = sectionData.getByteArray("Data");
if (blocks.length < 4096) blocks = Arrays.copyOf(blocks, 4096);
if (blockLight.length < 2048) blockLight = Arrays.copyOf(blockLight, 2048);
if (skyLight.length < 2048) skyLight = Arrays.copyOf(skyLight, 2048);
if (data.length < 2048) data = Arrays.copyOf(data, 2048);
}
public int getSectionY() {
return sectionY;
}
public BlockState getBlockState(int x, int y, int z) {
x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16)
int blockByteIndex = y * 256 + z * 16 + x;
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
int blockId = this.blocks[blockByteIndex] & 0xFF;
if (this.add.length > blockHalfByteIndex) {
blockId = blockId | (getByteHalf(this.add[blockHalfByteIndex], largeHalf) << 8);
}
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
String forgeIdMapping = forgeBlockIdMapper.apply(blockId);
if (forgeIdMapping != null) {
return blockIdMapper.get(forgeIdMapping, blockId, blockData);
} else {
return blockIdMapper.get(blockId, blockData);
}
}
public String getBlockIdMeta(Vector3i pos) {
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
int y = pos.getY() & 0xF;
int z = pos.getZ() & 0xF;
int blockByteIndex = y * 256 + z * 16 + x;
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
int blockId = this.blocks[blockByteIndex] & 0xFF;
if (this.add.length > blockHalfByteIndex) {
blockId = blockId | (getByteHalf(this.add[blockHalfByteIndex], largeHalf) << 8);
}
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
String forgeIdMapping = forgeBlockIdMapper.apply(blockId);
return blockId + ":" + blockData + " " + forgeIdMapping;
}
public LightData getLightData(int x, int y, int z, LightData target) {
x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16)
int blockByteIndex = y * 256 + z * 16 + x;
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
int blockLight = getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf);
int skyLight = getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf);
return target.set(skyLight, blockLight);
}
/**
* Extracts the 4 bits of the left (largeHalf = <code>true</code>) or the right (largeHalf = <code>false</code>) side of the byte stored in <code>value</code>.<br>
* The value is treated as an unsigned byte.
*/
private int getByteHalf(int value, boolean largeHalf) {
value = value & 0xFF;
if (largeHalf) {
value = value >> 4;
}
value = value & 0xF;
return value;
}
}
}

View File

@ -24,9 +24,7 @@
*/ */
package de.bluecolored.bluemap.core.mca; package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData; import de.bluecolored.bluemap.core.world.LightData;
@ -38,21 +36,15 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
public class ChunkAnvil113 extends MCAChunk { public class ChunkAnvil113 extends MCAChunk {
private static final MinecraftVersion VERSION = new MinecraftVersion(1, 13);
private BiomeMapper biomeIdMapper;
private boolean isGenerated; private boolean isGenerated;
private boolean hasLight; private boolean hasLight;
private Section[] sections; private Section[] sections;
private int[] biomes; private int[] biomes;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) { public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag); super(chunkTag);
this.biomeIdMapper = biomeIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level"); CompoundTag levelData = chunkTag.getCompoundTag("Level");
String status = levelData.getString("Status"); String status = levelData.getString("Status");
@ -124,14 +116,14 @@ public class ChunkAnvil113 extends MCAChunk {
} }
@Override @Override
public Biome getBiome(int x, int y, int z) { public int getBiome(int x, int y, int z) {
x = x & 0xF; // Math.floorMod(pos.getX(), 16) x = x & 0xF; // Math.floorMod(pos.getX(), 16)
z = z & 0xF; z = z & 0xF;
int biomeIntIndex = z * 16 + x; int biomeIntIndex = z * 16 + x;
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT; if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT.getNumeralId();
return biomeIdMapper.get(biomes[biomeIntIndex]); return biomes[biomeIntIndex];
} }
private class Section { private class Section {
@ -178,7 +170,7 @@ public class ChunkAnvil113 extends MCAChunk {
} }
} }
palette[i] = new BlockState(VERSION, id, properties); palette[i] = new BlockState(id, properties);
} }
} else { } else {
this.palette = new BlockState[0]; this.palette = new BlockState[0];

View File

@ -24,9 +24,7 @@
*/ */
package de.bluecolored.bluemap.core.mca; package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData; import de.bluecolored.bluemap.core.world.LightData;
@ -38,21 +36,15 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
public class ChunkAnvil115 extends MCAChunk { public class ChunkAnvil115 extends MCAChunk {
private static final MinecraftVersion VERSION = new MinecraftVersion(1, 15);
private BiomeMapper biomeIdMapper;
private boolean isGenerated; private boolean isGenerated;
private boolean hasLight; private boolean hasLight;
private Section[] sections; private Section[] sections;
private int[] biomes; private int[] biomes;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) { public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag); super(chunkTag);
this.biomeIdMapper = biomeIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level"); CompoundTag levelData = chunkTag.getCompoundTag("Level");
String status = levelData.getString("Status"); String status = levelData.getString("Status");
@ -124,16 +116,16 @@ public class ChunkAnvil115 extends MCAChunk {
} }
@Override @Override
public Biome getBiome(int x, int y, int z) { public int getBiome(int x, int y, int z) {
x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16) x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
z = (z & 0xF) / 4; z = (z & 0xF) / 4;
y = y / 4; y = y / 4;
int biomeIntIndex = y * 16 + z * 4 + x; int biomeIntIndex = y * 16 + z * 4 + x;
if (biomeIntIndex < 0) return Biome.DEFAULT; if (biomeIntIndex < 0) return Biome.DEFAULT.getNumeralId();
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT; if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT.getNumeralId();
return biomeIdMapper.get(biomes[biomeIntIndex]); return biomes[biomeIntIndex];
} }
private static class Section { private static class Section {
@ -180,7 +172,7 @@ public class ChunkAnvil115 extends MCAChunk {
} }
} }
palette[i] = new BlockState(VERSION, id, properties); palette[i] = new BlockState(id, properties);
} }
} else { } else {
this.palette = new BlockState[0]; this.palette = new BlockState[0];

View File

@ -24,9 +24,7 @@
*/ */
package de.bluecolored.bluemap.core.mca; package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData; import de.bluecolored.bluemap.core.world.LightData;
@ -39,10 +37,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
public class ChunkAnvil116 extends MCAChunk { public class ChunkAnvil116 extends MCAChunk {
private static final MinecraftVersion VERSION = new MinecraftVersion(1, 16);
private BiomeMapper biomeIdMapper;
private boolean isGenerated; private boolean isGenerated;
private boolean hasLight; private boolean hasLight;
@ -52,11 +46,9 @@ public class ChunkAnvil116 extends MCAChunk {
private int[] biomes; private int[] biomes;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) { public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag); super(chunkTag);
this.biomeIdMapper = biomeIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level"); CompoundTag levelData = chunkTag.getCompoundTag("Level");
String status = levelData.getString("Status"); String status = levelData.getString("Status");
@ -138,8 +130,8 @@ public class ChunkAnvil116 extends MCAChunk {
} }
@Override @Override
public Biome getBiome(int x, int y, int z) { public int getBiome(int x, int y, int z) {
if (biomes.length < 16) return Biome.DEFAULT; if (biomes.length < 16) return Biome.DEFAULT.getNumeralId();
x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16) x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
z = (z & 0xF) / 4; z = (z & 0xF) / 4;
@ -150,7 +142,7 @@ public class ChunkAnvil116 extends MCAChunk {
if (biomeIntIndex >= biomes.length) biomeIntIndex -= (((biomeIntIndex - biomes.length) >> 4) + 1) * 16; if (biomeIntIndex >= biomes.length) biomeIntIndex -= (((biomeIntIndex - biomes.length) >> 4) + 1) * 16;
if (biomeIntIndex < 0) biomeIntIndex -= (biomeIntIndex >> 4) * 16; if (biomeIntIndex < 0) biomeIntIndex -= (biomeIntIndex >> 4) * 16;
return biomeIdMapper.get(biomes[biomeIntIndex]); return biomes[biomeIntIndex];
} }
@Override @Override
@ -213,7 +205,7 @@ public class ChunkAnvil116 extends MCAChunk {
} }
} }
palette[i] = new BlockState(VERSION, id, properties); palette[i] = new BlockState(id, properties);
} }
} else { } else {
this.palette = new BlockState[0]; this.palette = new BlockState[0];

View File

@ -48,8 +48,8 @@ public class EmptyChunk extends MCAChunk {
} }
@Override @Override
public Biome getBiome(int x, int y, int z) { public int getBiome(int x, int y, int z) {
return Biome.DEFAULT; return Biome.DEFAULT.getNumeralId();
} }
} }

View File

@ -24,7 +24,6 @@
*/ */
package de.bluecolored.bluemap.core.mca; package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.Chunk; import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.LightData; import de.bluecolored.bluemap.core.world.LightData;
@ -59,7 +58,7 @@ public abstract class MCAChunk implements Chunk {
public abstract LightData getLightData(int x, int y, int z, LightData target); public abstract LightData getLightData(int x, int y, int z, LightData target);
@Override @Override
public abstract Biome getBiome(int x, int y, int z); public abstract int getBiome(int x, int y, int z);
@Override @Override
public int getMaxY(int x, int z) { public int getMaxY(int x, int z) {
@ -73,11 +72,10 @@ public abstract class MCAChunk implements Chunk {
public static MCAChunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) throws IOException { public static MCAChunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) throws IOException {
int version = chunkTag.getInt("DataVersion"); int version = chunkTag.getInt("DataVersion");
if (version < 1400) return new ChunkAnvil112(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper(), world.getBlockIdMapper(), world::getForgeBlockIdMapping); if (version < 2200) return new ChunkAnvil113(chunkTag, ignoreMissingLightData);
if (version < 2200) return new ChunkAnvil113(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper()); if (version < 2500) return new ChunkAnvil115(chunkTag, ignoreMissingLightData);
if (version < 2500) return new ChunkAnvil115(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper()); return new ChunkAnvil116(chunkTag, ignoreMissingLightData);
return new ChunkAnvil116(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper());
} }
public static MCAChunk empty() { public static MCAChunk empty() {

View File

@ -29,20 +29,13 @@ import com.flowpowered.math.vector.Vector3i;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.debug.DebugDump; import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.extensions.*; import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper; import de.bluecolored.bluemap.core.world.Grid;
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper; import de.bluecolored.bluemap.core.world.World;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import de.bluecolored.bluemap.core.util.ArrayPool;
import de.bluecolored.bluemap.core.util.math.VectorM2i;
import de.bluecolored.bluemap.core.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 java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -58,62 +51,28 @@ public class MCAWorld implements World {
@DebugDump private final UUID uuid; @DebugDump private final UUID uuid;
@DebugDump private final Path worldFolder; @DebugDump private final Path worldFolder;
private final MinecraftVersion minecraftVersion;
@DebugDump private final String name; @DebugDump private final String name;
@DebugDump private final Vector3i spawnPoint; @DebugDump private final Vector3i spawnPoint;
@DebugDump private final boolean ignoreMissingLightData;
private final LoadingCache<Vector2i, MCARegion> regionCache; private final LoadingCache<Vector2i, MCARegion> regionCache;
private final LoadingCache<Vector2i, MCAChunk> chunkCache; private final LoadingCache<Vector2i, MCAChunk> chunkCache;
private BlockIdMapper blockIdMapper;
private BlockPropertiesMapper blockPropertiesMapper;
private BiomeMapper biomeMapper;
private final Map<String, List<BlockStateExtension>> blockStateExtensions;
@DebugDump private final boolean ignoreMissingLightData;
private final Map<Integer, String> forgeBlockMappings;
private MCAWorld( private MCAWorld(
Path worldFolder, Path worldFolder,
UUID uuid, UUID uuid,
MinecraftVersion minecraftVersion,
String name, String name,
Vector3i spawnPoint, Vector3i spawnPoint,
BlockIdMapper blockIdMapper,
BlockPropertiesMapper blockPropertiesMapper,
BiomeMapper biomeMapper,
boolean ignoreMissingLightData boolean ignoreMissingLightData
) { ) {
this.uuid = uuid; this.uuid = uuid;
this.worldFolder = worldFolder; this.worldFolder = worldFolder;
this.minecraftVersion = minecraftVersion;
this.name = name; this.name = name;
this.spawnPoint = spawnPoint; this.spawnPoint = spawnPoint;
this.blockIdMapper = blockIdMapper;
this.blockPropertiesMapper = blockPropertiesMapper;
this.biomeMapper = biomeMapper;
this.ignoreMissingLightData = ignoreMissingLightData; this.ignoreMissingLightData = ignoreMissingLightData;
this.forgeBlockMappings = new HashMap<>();
this.blockStateExtensions = new HashMap<>();
registerBlockStateExtension(new SnowyExtension(minecraftVersion));
registerBlockStateExtension(new StairShapeExtension());
registerBlockStateExtension(new FireExtension());
registerBlockStateExtension(new RedstoneExtension());
registerBlockStateExtension(new DoorExtension(minecraftVersion));
registerBlockStateExtension(new NetherFenceConnectExtension());
registerBlockStateExtension(new TripwireConnectExtension());
registerBlockStateExtension(new WallConnectExtension());
registerBlockStateExtension(new WoodenFenceConnectExtension(minecraftVersion));
registerBlockStateExtension(new GlassPaneConnectExtension());
registerBlockStateExtension(new DoublePlantExtension(minecraftVersion));
registerBlockStateExtension(new DoubleChestExtension());
this.regionCache = Caffeine.newBuilder() this.regionCache = Caffeine.newBuilder()
.executor(BlueMap.THREAD_POOL) .executor(BlueMap.THREAD_POOL)
.maximumSize(100) .maximumSize(100)
@ -130,34 +89,6 @@ public class MCAWorld implements World {
public BlockState getBlockState(Vector3i pos) { public BlockState getBlockState(Vector3i pos) {
return getChunk(pos.getX() >> 4, pos.getZ() >> 4).getBlockState(pos.getX(), pos.getY(), pos.getZ()); return getChunk(pos.getX() >> 4, pos.getZ() >> 4).getBlockState(pos.getX(), pos.getY(), pos.getZ());
} }
@Override
public Biome getBiome(int x, int y, int z) {
return getChunk(x >> 4, z >> 4).getBiome(x, y, z);
}
@Override
public BlockState getBlockState(int x, int y, int z) {
MCAChunk chunk = getChunk(x >> 4, z >> 4);
BlockState blockState = chunk.getBlockState(x, y, z);
if (chunk instanceof ChunkAnvil112) { // only use extensions if old format chunk (1.12) in the new format block-states are saved with extensions
List<BlockStateExtension> applicableExtensions = blockStateExtensions.getOrDefault(blockState.getFullId(), Collections.emptyList());
if (!applicableExtensions.isEmpty()) {
Vector3i pos = new Vector3i(x, y, z);
for (BlockStateExtension ext : applicableExtensions) {
blockState = ext.extend(this, pos, blockState);
}
}
}
return blockState;
}
@Override
public BlockProperties getBlockProperties(BlockState blockState) {
return blockPropertiesMapper.get(blockState);
}
@Override @Override
public MCAChunk getChunkAtBlock(int x, int y, int z) { public MCAChunk getChunkAtBlock(int x, int y, int z) {
@ -166,19 +97,19 @@ public class MCAWorld implements World {
@Override @Override
public MCAChunk getChunk(int x, int z) { public MCAChunk getChunk(int x, int z) {
return getChunk(new Vector2i(x, z)); return getChunk(vec2i(x, z));
} }
public MCAChunk getChunk(Vector2i pos) { private MCAChunk getChunk(Vector2i pos) {
return chunkCache.get(pos); return chunkCache.get(pos);
} }
@Override @Override
public MCARegion getRegion(int x, int z) { public MCARegion getRegion(int x, int z) {
return getRegion(new Vector2i(x, z)); return getRegion(vec2i(x, z));
} }
public MCARegion getRegion(Vector2i pos) { private MCARegion getRegion(Vector2i pos) {
return regionCache.get(pos); return regionCache.get(pos);
} }
@ -264,42 +195,10 @@ public class MCAWorld implements World {
public void cleanUpChunkCache() { public void cleanUpChunkCache() {
chunkCache.cleanUp(); chunkCache.cleanUp();
} }
public BlockIdMapper getBlockIdMapper() {
return blockIdMapper;
}
public BlockPropertiesMapper getBlockPropertiesMapper() {
return blockPropertiesMapper;
}
public BiomeMapper getBiomeIdMapper() {
return biomeMapper;
}
public void setBlockIdMapper(BlockIdMapper blockIdMapper) {
this.blockIdMapper = blockIdMapper;
}
public void setBlockPropertiesMapper(BlockPropertiesMapper blockPropertiesMapper) {
this.blockPropertiesMapper = blockPropertiesMapper;
}
public void setBiomeMapper(BiomeMapper biomeMapper) {
this.biomeMapper = biomeMapper;
}
public Path getWorldFolder() { public Path getWorldFolder() {
return worldFolder; return worldFolder;
} }
public String getForgeBlockIdMapping(int id) {
return forgeBlockMappings.get(id);
}
public MinecraftVersion getMinecraftVersion() {
return minecraftVersion;
}
private Path getRegionFolder() { private Path getRegionFolder() {
return worldFolder.resolve("region"); return worldFolder.resolve("region");
@ -309,12 +208,6 @@ public class MCAWorld implements World {
return getRegionFolder().resolve("r." + regionX + "." + regionZ + ".mca").toFile(); return getRegionFolder().resolve("r." + regionX + "." + regionZ + ".mca").toFile();
} }
private void registerBlockStateExtension(BlockStateExtension extension) {
for (String id : extension.getAffectedBlockIds()) {
this.blockStateExtensions.computeIfAbsent(id, t -> new ArrayList<>()).add(extension);
}
}
private MCARegion loadRegion(Vector2i regionPos) { private MCARegion loadRegion(Vector2i regionPos) {
return loadRegion(regionPos.getX(), regionPos.getY()); return loadRegion(regionPos.getX(), regionPos.getY());
} }
@ -356,11 +249,11 @@ public class MCAWorld implements World {
return MCAChunk.empty(); return MCAChunk.empty();
} }
public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion version, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, BiomeMapper biomeIdMapper) throws IOException { public static MCAWorld load(Path worldFolder, UUID uuid) throws IOException {
return load(worldFolder, uuid, version, blockIdMapper, blockPropertiesMapper, biomeIdMapper, null, false); return load(worldFolder, uuid, null, false);
} }
public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion version, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, BiomeMapper biomeIdMapper, String name, boolean ignoreMissingLightData) throws IOException { public static MCAWorld load(Path worldFolder, UUID uuid, String name, boolean ignoreMissingLightData) throws IOException {
try { try {
StringBuilder subDimensionName = new StringBuilder(); StringBuilder subDimensionName = new StringBuilder();
@ -392,37 +285,15 @@ public class MCAWorld implements World {
levelData.getInt("SpawnX"), levelData.getInt("SpawnX"),
levelData.getInt("SpawnY"), levelData.getInt("SpawnY"),
levelData.getInt("SpawnZ") levelData.getInt("SpawnZ")
); );
MCAWorld world = new MCAWorld( return new MCAWorld(
worldFolder, worldFolder,
uuid, uuid,
version,
name, name,
spawnPoint, spawnPoint,
blockIdMapper,
blockPropertiesMapper,
biomeIdMapper,
ignoreMissingLightData ignoreMissingLightData
); );
try {
CompoundTag fmlTag = level.getCompoundTag("FML");
if (fmlTag == null) fmlTag = level.getCompoundTag("fml");
ListTag<? extends Tag<?>> blockIdReg = fmlTag.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);
} }
@ -437,4 +308,17 @@ public class MCAWorld implements World {
'}'; '}';
} }
private static final int VEC_2I_CACHE_SIZE = 0x4000;
private static final int VEC_2I_CACHE_MASK = VEC_2I_CACHE_SIZE - 1;
private static final Vector2i[] VEC_2I_CACHE = new Vector2i[VEC_2I_CACHE_SIZE];
private static Vector2i vec2i(int x, int y) {
int cacheIndex = (x * 1456 ^ y * 948375892) & VEC_2I_CACHE_MASK;
Vector2i possibleMatch = VEC_2I_CACHE[cacheIndex];
if (possibleMatch != null && possibleMatch.getX() == x && possibleMatch.getY() == y)
return possibleMatch;
return VEC_2I_CACHE[cacheIndex] = new Vector2i(x, y);
}
} }

View File

@ -1,57 +0,0 @@
/*
* 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.mca.extensions;
import java.util.Set;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
public abstract class ConnectExtension implements BlockStateExtension {
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
return state
.with("north", String.valueOf(connectsTo(world, pos.add(Direction.NORTH.toVector()))))
.with("east", String.valueOf(connectsTo(world, pos.add(Direction.EAST.toVector()))))
.with("south", String.valueOf(connectsTo(world, pos.add(Direction.SOUTH.toVector()))))
.with("west", String.valueOf(connectsTo(world, pos.add(Direction.WEST.toVector()))));
}
public boolean connectsTo(MCAWorld world, Vector3i pos) {
return connectsTo(world, pos, world.getBlockState(pos));
}
public boolean connectsTo(MCAWorld world, Vector3i pos, BlockState block) {
return getAffectedBlockIds().contains(block.getFullId());
}
@Override
public abstract Set<String> getAffectedBlockIds();
}

View File

@ -1,41 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.BlockState;
public abstract class ConnectSameOrFullBlockExtension extends ConnectExtension {
@Override
public boolean connectsTo(MCAWorld world, Vector3i pos, BlockState block) {
if (super.connectsTo(world, pos, block)) return true;
return world.getBlockPropertiesMapper().get(block).isCulling();
}
}

View File

@ -1,99 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
public class DoorExtension implements BlockStateExtension {
private final Set<String> affectedBlockIds;
public DoorExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:wooden_door",
"minecraft:iron_door",
"minecraft:spruce_door",
"minecraft:birch_door",
"minecraft:jungle_door",
"minecraft:acacia_door",
"minecraft:dark_oak_door"
));
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:oak_door",
"minecraft:iron_door",
"minecraft:spruce_door",
"minecraft:birch_door",
"minecraft:jungle_door",
"minecraft:acacia_door",
"minecraft:dark_oak_door"
));
}
}
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState otherDoor;
boolean isLower = Objects.equals(state.getProperties().get("half"), "lower");
if (isLower) {
otherDoor = world.getBlockState(pos.add(Direction.UP.toVector()));
} else {
otherDoor = world.getBlockState(pos.add(Direction.DOWN.toVector()));
}
//copy all properties from the other door
for (Entry<String, String> prop : otherDoor.getProperties().entrySet()) {
if (
!state.getProperties().containsKey(prop.getKey()) ||
(isLower && prop.getKey().equals("hinge")) ||
(isLower && prop.getKey().equals("powered")) ||
(!isLower && prop.getKey().equals("open")) ||
(!isLower && prop.getKey().equals("facing"))
) {
state = state.with(prop.getKey(), prop.getValue());
}
}
return state;
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,61 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class DoubleChestExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:chest",
"minecraft:trapped_chest"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
Direction dir = Direction.fromString(state.getProperties().getOrDefault("facing", "north"));
BlockState left = world.getBlockState(pos.add(dir.left().toVector()));
if (left.getFullId().equals(state.getFullId())) return state.with("type", "right");
BlockState right = world.getBlockState(pos.add(dir.right().toVector()));
if (right.getFullId().equals(state.getFullId())) return state.with("type", "left");
return state.with("type", "single");
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,71 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.*;
public class DoublePlantExtension implements BlockStateExtension {
private final Set<String> affectedBlockIds;
public DoublePlantExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Collections.singletonList(
"minecraft:double_plant"
));
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:sunflower",
"minecraft:lilac",
"minecraft:tall_grass",
"minecraft:large_fern",
"minecraft:rose_bush",
"minecraft:peony"
));
}
}
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
if (Objects.equals(state.getProperties().get("half"), "upper")) {
BlockState otherPlant = world.getBlockState(pos.add(Direction.DOWN.toVector()));
return otherPlant.with("half", "upper");
}
return state;
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class FireExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList(
"minecraft:fire"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState below = world.getBlockState(pos.add(0, -1, 0));
boolean isOnGround = world.getBlockPropertiesMapper().get(below).isCulling();
for (Direction dir : Direction.values()) {
if (dir != Direction.DOWN) {
if (!isOnGround) {
BlockState neighbor = world.getBlockState(pos.add(dir.toVector()));
state = state.with(dir.name().toLowerCase(), String.valueOf(!world.getBlockPropertiesMapper().get(neighbor).isCulling()));
} else {
state = state.with(dir.name().toLowerCase(), "false");
}
}
}
return state;
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,58 +0,0 @@
/*
* 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.mca.extensions;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class GlassPaneConnectExtension extends ConnectSameOrFullBlockExtension {
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:glass_pane",
"minecraft:white_stained_glass_pane",
"minecraft:orange_stained_glass_pane",
"minecraft:magenta_stained_glass_pane",
"minecraft:light_blue_white_stained_glass_pane",
"minecraft:yellow_stained_glass_pane",
"minecraft:lime_stained_glass_pane",
"minecraft:pink_stained_glass_pane",
"minecraft:gray_stained_glass_pane",
"minecraft:light_gray_stained_glass_pane",
"minecraft:cyan_stained_glass_pane",
"minecraft:purple_stained_glass_pane",
"minecraft:blue_stained_glass_pane",
"minecraft:green_stained_glass_pane",
"minecraft:red_stained_glass_pane",
"minecraft:black_stained_glass_pane",
"minecraft:iron_bars"
));
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,43 +0,0 @@
/*
* 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.mca.extensions;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class NetherFenceConnectExtension extends ConnectSameOrFullBlockExtension {
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList(
"minecraft:nether_brick_fence"
));
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,96 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class RedstoneExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList(
"minecraft:redstone_wire"
));
private static final Set<String> CONNECTIBLE = new HashSet<>(Arrays.asList(
"minecraft:redstone_wire",
"minecraft:redstone_wall_torch",
"minecraft:redstone_torch",
"minecraft:stone_button",
"minecraft:oak_button",
"minecraft:stone_button",
"minecraft:lever",
"minecraft:stone_pressure_plate",
"minecraft:oak_pressure_plate",
"minecraft:light_weighted_pressure_plate",
"minecraft:heavy_weighted_pressure_plate"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState up = world.getBlockState(pos.add(0, 1, 0));
boolean upBlocking = !up.equals(BlockState.AIR);
state = state
.with("north", connection(world, pos, upBlocking, Direction.NORTH))
.with("east", connection(world, pos, upBlocking, Direction.EAST))
.with("south", connection(world, pos, upBlocking, Direction.SOUTH))
.with("west", connection(world, pos, upBlocking, Direction.WEST));
return state;
}
private String connection(MCAWorld world, Vector3i pos, boolean upBlocking, Direction direction) {
Vector3i directionVector = direction.toVector();
BlockState next = world.getBlockState(pos.add(directionVector));
if (CONNECTIBLE.contains(next.getFullId())) return "side";
if (next.equals(BlockState.AIR)) {
BlockState nextdown = world.getBlockState(pos.add(directionVector.getX(), directionVector.getY() - 1, directionVector.getZ()));
if (nextdown.getFullId().equals("minecraft:redstone_wire")) return "side";
}
if (!upBlocking) {
BlockState nextup = world.getBlockState(pos.add(directionVector.getX(), directionVector.getY() + 1, directionVector.getZ()));
if (nextup.getFullId().equals("minecraft:redstone_wire")) return "up";
}
return "none";
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,77 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class SnowyExtension implements BlockStateExtension {
private final Set<String> affectedBlockIds;
private final String snowLayerId;
private final String snowBlockId;
public SnowyExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:grass",
"minecraft:mycelium"
));
snowLayerId = "minecraft:snow_layer";
snowBlockId = "minecraft:snow";
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:grass_block",
"minecraft:podzol"
));
snowLayerId = "minecraft:snow";
snowBlockId = "minecraft:snow_block";
}
}
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState above = world.getBlockState(pos.add(0, 1, 0));
if (above.getFullId().equals(snowLayerId) || above.getFullId().equals(snowBlockId)) {
return state.with("snowy", "true");
} else {
return state.with("snowy", "false");
}
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,118 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class StairShapeExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:oak_stairs",
"minecraft:cobblestone_stairs",
"minecraft:brick_stairs",
"minecraft:stone_brick_stairs",
"minecraft:nether_brick_stairs",
"minecraft:sandstone_stairs",
"minecraft:spruce_stairs",
"minecraft:birch_stairs",
"minecraft:jungle_stairs",
"minecraft:quartz_stairs",
"minecraft:acacia_stairs",
"minecraft:dark_oak_stairs",
"minecraft:red_sandstone_stairs",
"minecraft:purpur_stairs"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
try {
Direction facing = Direction.fromString(state.getProperties().get("facing"));
BlockState back = world.getBlockState(pos.add(facing.toVector()));
if (isStairs(back) && state.getProperties().get("half").equals(back.getProperties().get("half"))) {
Direction backFacing = Direction.fromString(back.getProperties().get("facing"));
if (facing.getAxis() != backFacing.getAxis()){
BlockState next = world.getBlockState(pos.add(backFacing.opposite().toVector()));
if (!isStairs(next) || !isEqualStairs(state, next)) {
if (backFacing == facing.left()){
return state.with("shape", "outer_left");
}
return state.with("shape", "outer_right");
}
}
}
BlockState front = world.getBlockState(pos.add(facing.opposite().toVector()));
if (isStairs(front) && state.getProperties().get("half").equals(front.getProperties().get("half"))) {
Direction frontFacing = Direction.fromString(front.getProperties().get("facing"));
if (facing.getAxis() != frontFacing.getAxis()){
BlockState next = world.getBlockState(pos.add(frontFacing.toVector()));
if (!isStairs(next) || !isEqualStairs(state, next)) {
if (frontFacing == facing.left()){
return state.with("shape", "inner_left");
}
return state.with("shape", "inner_right");
}
}
}
return state.with("shape", "straight");
} catch (IllegalArgumentException | NullPointerException ex) {
return state.with("shape", "straight");
}
}
private boolean isStairs(BlockState state) {
return AFFECTED_BLOCK_IDS.contains(state.getFullId());
}
private boolean isEqualStairs(BlockState stair1, BlockState stair2) {
return
stair1.getProperties().get("facing").equals(stair2.getProperties().get("facing")) &&
stair1.getProperties().get("half").equals(stair2.getProperties().get("half"));
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,65 +0,0 @@
/*
* 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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class WallConnectExtension extends ConnectSameOrFullBlockExtension {
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:cobblestone_wall",
"minecraft:mossy_cobblestone_wall"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
state = super.extend(world, pos, state);
if (
Objects.equals(state.getProperties().get("north"), state.getProperties().get("south")) &&
Objects.equals(state.getProperties().get("east"), state.getProperties().get("west")) &&
!Objects.equals(state.getProperties().get("north"), state.getProperties().get("east")) &&
!connectsTo(world, pos.add(Direction.UP.toVector()))
) {
return state.with("up", "false");
} else {
return state.with("up", "true");
}
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,64 +0,0 @@
/*
* 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.mca.extensions;
import de.bluecolored.bluemap.core.MinecraftVersion;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class WoodenFenceConnectExtension extends ConnectSameOrFullBlockExtension {
private final Set<String> affectedBlockIds;
public WoodenFenceConnectExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:fence",
"minecraft:spruce_fence",
"minecraft:birch_fence",
"minecraft:jungle_fence",
"minecraft:dark_oak_fence",
"minecraft:acacia_fence"
));
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:oak_fence",
"minecraft:spruce_fence",
"minecraft:birch_fence",
"minecraft:jungle_fence",
"minecraft:dark_oak_fence",
"minecraft:acacia_fence"
));
}
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,35 +0,0 @@
/*
* 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.mca.mapping;
import de.bluecolored.bluemap.core.world.BlockState;
public interface BlockIdMapper {
BlockState get(int id, int meta);
BlockState get(String id, int numeralId, int meta);
}

View File

@ -1,35 +0,0 @@
/*
* 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.mca.mapping;
import de.bluecolored.bluemap.core.world.BlockProperties;
import de.bluecolored.bluemap.core.world.BlockState;
@FunctionalInterface
public interface BlockPropertiesMapper {
BlockProperties get(BlockState blockState);
}

View File

@ -28,7 +28,6 @@ import com.flowpowered.math.imaginary.Quaternionf;
import com.flowpowered.math.matrix.Matrix3f; import com.flowpowered.math.matrix.Matrix3f;
import com.flowpowered.math.vector.Vector2f; import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector3f; import com.flowpowered.math.vector.Vector3f;
import de.bluecolored.bluemap.core.util.MathUtils;
@Deprecated @Deprecated
public class Face { public class Face {
@ -51,7 +50,7 @@ public class Face {
this.materialIndex = materialIndex; this.materialIndex = materialIndex;
this.n1 = getFaceNormal(); this.n1 = calculateSurfaceNormal(new VectorM3f(0, 0, 0));
this.n2 = new VectorM3f(n1); this.n2 = new VectorM3f(n1);
this.n3 = new VectorM3f(n1); this.n3 = new VectorM3f(n1);
this.normalizedNormals = true; this.normalizedNormals = true;
@ -202,10 +201,6 @@ public class Face {
this.materialIndex = materialIndex; this.materialIndex = materialIndex;
} }
private VectorM3f getFaceNormal() {
return MathUtils.getSurfaceNormal(p1, p2, p3);
}
private void normalizeNormals() { private void normalizeNormals() {
if (normalizedNormals) return; if (normalizedNormals) return;
@ -216,4 +211,31 @@ public class Face {
normalizedNormals = true; normalizedNormals = true;
} }
private VectorM3f calculateSurfaceNormal(
VectorM3f target
){
double
p1x = p1.x, p1y = p1.y, p1z = p1.z,
p2x = p2.x, p2y = p2.y, p2z = p2.z,
p3x = p3.x, p3y = p3.y, p3z = p3.z;
p2x -= p1x; p2y -= p1y; p2z -= p1z;
p3x -= p1x; p3y -= p1y; p3z -= p1z;
p1x = p2y * p3z - p2z * p3y;
p1y = p2z * p3x - p2x * p3z;
p1z = p2x * p3y - p2y * p3x;
double length = Math.sqrt(p1x * p1x + p1y * p1y + p1z * p1z);
p1x /= length;
p1y /= length;
p1z /= length;
target.x = (float) p1x;
target.y = (float) p1y;
target.z = (float) p1z;
return target;
}
} }

View File

@ -22,13 +22,52 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.mca.mapping; package de.bluecolored.bluemap.core.resourcepack;
import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.Biome;
import org.spongepowered.configurate.ConfigurationNode;
@FunctionalInterface import java.util.Map.Entry;
public interface BiomeMapper {
public class BiomeConfig {
private Biome[] biomes;
public BiomeConfig() {
biomes = new Biome[10];
}
public void load(ConfigurationNode node) {
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String id = e.getKey().toString();
Biome biome = Biome.create(id, e.getValue());
int numeralId = biome.getNumeralId();
ensureAvailability(numeralId);
biomes[numeralId] = biome;
}
}
public Biome getBiome(int id) {
if (id < biomes.length) {
Biome biome = biomes[id];
return biome != null ? biome : Biome.DEFAULT;
}
return Biome.DEFAULT;
}
private void ensureAvailability(int id) {
if (id >= biomes.length) {
int newSize = biomes.length;
do {
newSize = (int) (newSize * 1.5) + 1;
} while (id >= newSize);
Biome[] newArray = new Biome[newSize];
System.arraycopy(biomes, 0, newArray, 0, biomes.length);
biomes = newArray;
}
}
Biome get(int id);
} }

View File

@ -29,7 +29,7 @@ import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.util.ConfigUtils; import de.bluecolored.bluemap.core.util.ConfigUtils;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.ConfigurationNode;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -40,21 +40,16 @@ import java.util.Map.Entry;
@DebugDump @DebugDump
public class BlockColorCalculatorFactory { public class BlockColorCalculatorFactory {
private BufferedImage foliageMap; private final int[] foliageMap = new int[65536];
private BufferedImage grassMap; private final int[] grassMap = new int[65536];
private final Map<String, ColorFunction> blockColorMap; private final Map<String, ColorFunction> blockColorMap;
public BlockColorCalculatorFactory(BufferedImage foliageMap, BufferedImage grassMap) { public BlockColorCalculatorFactory() {
this.foliageMap = foliageMap;
this.grassMap = grassMap;
this.blockColorMap = new HashMap<>(); this.blockColorMap = new HashMap<>();
} }
public void loadColorConfig(ConfigurationNode colorConfig) { public void load(ConfigurationNode colorConfig) {
blockColorMap.clear();
for (Entry<Object, ? extends ConfigurationNode> entry : colorConfig.childrenMap().entrySet()){ for (Entry<Object, ? extends ConfigurationNode> entry : colorConfig.childrenMap().entrySet()){
String key = entry.getKey().toString(); String key = entry.getKey().toString();
String value = entry.getValue().getString(""); String value = entry.getValue().getString("");
@ -85,19 +80,11 @@ public class BlockColorCalculatorFactory {
} }
public void setFoliageMap(BufferedImage foliageMap) { public void setFoliageMap(BufferedImage foliageMap) {
this.foliageMap = foliageMap; foliageMap.getRGB(0, 0, 256, 256, this.foliageMap, 0, 256);
}
public BufferedImage getFoliageMap() {
return foliageMap;
} }
public void setGrassMap(BufferedImage grassMap) { public void setGrassMap(BufferedImage grassMap) {
this.grassMap = grassMap; grassMap.getRGB(0, 0, 256, 256, this.grassMap, 0, 256);
}
public BufferedImage getGrassMap() {
return grassMap;
} }
public BlockColorCalculator createCalculator() { public BlockColorCalculator createCalculator() {
@ -106,14 +93,14 @@ public class BlockColorCalculatorFactory {
@FunctionalInterface @FunctionalInterface
private interface ColorFunction { private interface ColorFunction {
Color invoke(BlockColorCalculator calculator, Block block, Color target); Color invoke(BlockColorCalculator calculator, BlockNeighborhood block, Color target);
} }
public class BlockColorCalculator { public class BlockColorCalculator {
private final Color tempColor = new Color(); private final Color tempColor = new Color();
public Color getBlockColor(Block block, Color target) { public Color getBlockColor(BlockNeighborhood block, Color target) {
String blockId = block.getBlockState().getFullId(); String blockId = block.getBlockState().getFullId();
ColorFunction colorFunction = blockColorMap.get(blockId); ColorFunction colorFunction = blockColorMap.get(blockId);
@ -123,7 +110,7 @@ public class BlockColorCalculatorFactory {
return colorFunction.invoke(this, block, target); return colorFunction.invoke(this, block, target);
} }
public Color getRedstoneColor(Block block, Color target) { public Color getRedstoneColor(BlockNeighborhood block, Color target) {
String powerString = block.getBlockState().getProperties().get("power"); String powerString = block.getBlockState().getProperties().get("power");
int power = 15; int power = 15;
@ -137,25 +124,23 @@ public class BlockColorCalculatorFactory {
); );
} }
public Color getWaterAverageColor(Block block, Color target) { public Color getWaterAverageColor(BlockNeighborhood block, Color target) {
target.set(0, 0, 0, 0, true); target.set(0, 0, 0, 0, true);
int x, y, z, int x, y, z,
minX = block.getX() - 2, minX = - 2,
maxX = block.getX() + 2, maxX = + 2,
minY = block.getY() - 1, minY = - 1,
maxY = block.getY() + 1, maxY = + 1,
minZ = block.getZ() - 2, minZ = - 2,
maxZ = block.getZ() + 2; maxZ = + 2;
Biome biome;
for (x = minX; x <= maxX; x++) { for (x = minX; x <= maxX; x++) {
for (y = minY; y <= maxY; y++) { for (y = minY; y <= maxY; y++) {
for (z = minZ; z <= maxZ; z++) { for (z = minZ; z <= maxZ; z++) {
target.add(block biome = block.getNeighborBlock(x, y, z).getBiome();
.getWorld() target.add(biome.getWaterColor());
.getBiome(x, y, z)
.getWaterColor()
);
} }
} }
} }
@ -163,28 +148,23 @@ public class BlockColorCalculatorFactory {
return target.flatten(); return target.flatten();
} }
public Color getFoliageAverageColor(Block block, Color target) { public Color getFoliageAverageColor(BlockNeighborhood block, Color target) {
target.set(0, 0, 0, 0, true); target.set(0, 0, 0, 0, true);
int x, y, z, int x, y, z,
minX = block.getX() - 2, minX = - 2,
maxX = block.getX() + 2, maxX = + 2,
minY = block.getY() - 1, minY = - 1,
maxY = block.getY() + 1, maxY = + 1,
minZ = block.getZ() - 2, minZ = - 2,
maxZ = block.getZ() + 2; maxZ = + 2;
int seaLevel = block.getWorld().getSeaLevel();
int blocksAboveSeaLevel;
Biome biome; Biome biome;
for (y = minY; y <= maxY; y++) { for (y = minY; y <= maxY; y++) {
blocksAboveSeaLevel = Math.max(block.getY() - seaLevel, 0);
for (x = minX; x <= maxX; x++) { for (x = minX; x <= maxX; x++) {
for (z = minZ; z <= maxZ; z++) { for (z = minZ; z <= maxZ; z++) {
biome = block.getWorld().getBiome(x, y, z); biome = block.getNeighborBlock(x, y, z).getBiome();
target.add(getFoliageColor(biome, blocksAboveSeaLevel, tempColor)); target.add(getFoliageColor(biome, tempColor));
} }
} }
} }
@ -192,33 +172,28 @@ public class BlockColorCalculatorFactory {
return target.flatten(); return target.flatten();
} }
public Color getFoliageColor(Biome biome, int blocksAboveSeaLevel, Color target) { public Color getFoliageColor(Biome biome, Color target) {
getColorFromMap(biome, blocksAboveSeaLevel, foliageMap, target); getColorFromMap(biome, foliageMap, 4764952, target);
return target.overlay(biome.getOverlayFoliageColor()); return target.overlay(biome.getOverlayFoliageColor());
} }
public Color getGrassAverageColor(Block block, Color target) { public Color getGrassAverageColor(BlockNeighborhood block, Color target) {
target.set(0, 0, 0, 0, true); target.set(0, 0, 0, 0, true);
int x, y, z, int x, y, z,
minX = block.getX() - 2, minX = - 2,
maxX = block.getX() + 2, maxX = + 2,
minY = block.getY() - 1, minY = - 1,
maxY = block.getY() + 1, maxY = + 1,
minZ = block.getZ() - 2, minZ = - 2,
maxZ = block.getZ() + 2; maxZ = + 2;
int seaLevel = block.getWorld().getSeaLevel();
int blocksAboveSeaLevel;
Biome biome; Biome biome;
for (y = minY; y <= maxY; y++) { for (y = minY; y <= maxY; y++) {
blocksAboveSeaLevel = Math.max(block.getY() - seaLevel, 0);
for (x = minX; x <= maxX; x++) { for (x = minX; x <= maxX; x++) {
for (z = minZ; z <= maxZ; z++) { for (z = minZ; z <= maxZ; z++) {
biome = block.getWorld().getBiome(x, y, z); biome = block.getNeighborBlock(x, y, z).getBiome();
target.add(getGrassColor(biome, blocksAboveSeaLevel, tempColor)); target.add(getGrassColor(biome, tempColor));
} }
} }
} }
@ -226,24 +201,22 @@ public class BlockColorCalculatorFactory {
return target.flatten(); return target.flatten();
} }
public Color getGrassColor(Biome biome, int blocksAboveSeaLevel, Color target) { public Color getGrassColor(Biome biome, Color target) {
getColorFromMap(biome, blocksAboveSeaLevel, grassMap, target); getColorFromMap(biome, grassMap, 0xff52952f, target);
return target.overlay(biome.getOverlayGrassColor()); return target.overlay(biome.getOverlayGrassColor());
} }
private void getColorFromMap(Biome biome, int blocksAboveSeaLevel, BufferedImage map, Color target) { private void getColorFromMap(Biome biome, int[] colorMap, int defaultColor, Color target) {
float adjTemp = (float) GenericMath.clamp(biome.getTemp() - (0.00166667 * blocksAboveSeaLevel), 0, 1); double temperature = GenericMath.clamp(biome.getTemp(), 0, 1);
float adjHumidity = (float) GenericMath.clamp(biome.getHumidity(), 0, 1) * adjTemp; double humidity = GenericMath.clamp(biome.getHumidity(), 0, 1) * temperature;
int x = (int) ((1 - adjTemp) * map.getWidth()); int x = (int) ((1.0 - temperature) * 255.0);
int y = (int) ((1 - adjHumidity) * map.getHeight()); int y = (int) ((1.0 - humidity) * 255.0);
int cValue = map.getRGB( int index = y << 8 | x;
GenericMath.clamp(x, 0, map.getWidth() - 1), int color = index >= colorMap.length ? defaultColor : colorMap[index];
GenericMath.clamp(y, 0, map.getHeight() - 1)
);
target.set(cValue); target.set(color);
} }
} }

View File

@ -0,0 +1,82 @@
/*
* 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.resourcepack;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.world.BlockProperties;
import de.bluecolored.bluemap.core.world.BlockState;
import org.spongepowered.configurate.ConfigurationNode;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class BlockPropertiesConfig {
private final Map<String, List<BlockStateMapping<BlockProperties>>> mappings;
public BlockPropertiesConfig() {
mappings = new ConcurrentHashMap<>();
}
public void load(ConfigurationNode node) {
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String key = e.getKey().toString();
try {
BlockState bsKey = BlockState.fromString(key);
BlockProperties.Builder bsValueBuilder = BlockProperties.builder();
readBool(e.getValue().node("culling"), bsValueBuilder::culling);
readBool(e.getValue().node("occluding"), bsValueBuilder::occluding);
readBool(e.getValue().node("alwaysWaterlogged"), bsValueBuilder::alwaysWaterlogged);
readBool(e.getValue().node("randomOffset"), bsValueBuilder::randomOffset);
BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValueBuilder.build());
mappings.computeIfAbsent(bsKey.getFullId(), k -> new LinkedList<>()).add(0, mapping);
} catch (IllegalArgumentException ex) {
Logger.global.logWarning("Loading BlockPropertiesConfig: Failed to parse BlockState from key '" + key + "'");
}
}
}
private void readBool(ConfigurationNode node, Consumer<Boolean> target) {
if (!node.virtual()) target.accept(node.getBoolean());
}
public BlockProperties getBlockProperties(BlockState from){
for (BlockStateMapping<BlockProperties> bm : mappings.getOrDefault(from.getFullId(), Collections.emptyList())){
if (bm.fitsTo(from)){
return bm.getMapping();
}
}
return BlockProperties.DEFAULT;
}
}

View File

@ -22,12 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.config; package de.bluecolored.bluemap.core.resourcepack;
import java.util.Map.Entry;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Map.Entry;
class BlockStateMapping<T> { class BlockStateMapping<T> {
private BlockState blockState; private BlockState blockState;
private T mapping; private T mapping;
@ -59,5 +59,5 @@ class BlockStateMapping<T> {
public T getMapping(){ public T getMapping(){
return mapping; return mapping;
} }
} }

View File

@ -24,83 +24,64 @@
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack;
import de.bluecolored.bluemap.core.MinecraftVersion; import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.debug.DebugDump; import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource.Builder; import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockstate.BlockStateResource;
import de.bluecolored.bluemap.core.resourcepack.blockstate.BlockStateResource.Builder;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.BluemapAssetOverrideFileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.BluemapAssetOverrideFileAccess;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.CaseInsensitiveFileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.CaseInsensitiveFileAccess;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.CombinedFileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.CombinedFileAccess;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.resourcepack.texture.TextureGallery;
import de.bluecolored.bluemap.core.util.Tristate;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockProperties;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.io.output.ByteArrayOutputStream;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.util.*; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/** /**
* Represents all resources (BlockStates / BlockModels and Textures) that are loaded and used to generate map-models. * Represents all resources (BlockStates / BlockModels and Textures) that are loaded and used to generate map-models.
*/ */
@DebugDump @DebugDump
public class ResourcePack { public class ResourcePack {
private static final String[] CONFIG_FILES = {
"blockColors.json",
"blockIds.json",
"blockProperties.json",
"biomes.json"
};
private final MinecraftVersion minecraftVersion;
protected final Map<String, BlockStateResource> blockStateResources;
protected final Map<String, BlockModelResource> blockModelResources;
protected final TextureGallery textures;
private final BlockColorCalculatorFactory blockColorCalculatorFactory;
private final Map<String, List<Resource>> configs;
private BufferedImage foliageMap; private final Map<String, BlockStateResource> blockStateResources;
private BufferedImage grassMap; private final Map<String, BlockModelResource> blockModelResources;
private final TextureGallery textures;
public ResourcePack(MinecraftVersion minecraftVersion) {
this.minecraftVersion = minecraftVersion; private final BlockPropertiesConfig blockPropertiesConfig;
private final BiomeConfig biomeConfig;
private final BlockColorCalculatorFactory blockColorCalculatorFactory;
private final LoadingCache<BlockState, BlockProperties> blockPropertiesCache;
public ResourcePack() {
blockStateResources = new HashMap<>(); blockStateResources = new HashMap<>();
blockModelResources = new HashMap<>(); blockModelResources = new HashMap<>();
textures = new TextureGallery(); textures = new TextureGallery();
foliageMap = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
foliageMap.setRGB(0, 0, 0xFF00FF00); blockPropertiesConfig = new BlockPropertiesConfig();
grassMap = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); biomeConfig = new BiomeConfig();
grassMap.setRGB(0, 0, 0xFF00FF00); blockColorCalculatorFactory = new BlockColorCalculatorFactory();
blockColorCalculatorFactory = new BlockColorCalculatorFactory(foliageMap, grassMap);
configs = new HashMap<>(); blockPropertiesCache = Caffeine.newBuilder()
} .executor(BlueMap.THREAD_POOL)
.maximumSize(10000)
/** .build(this::getBlockPropertiesNoCache);
* Returns all config-files found in the namespaces of the ResourcePack with that filename
*/
public Collection<Resource> getConfigAdditions(String configFileName){
return configs.getOrDefault(configFileName, Collections.emptyList());
}
/**
* See {@link TextureGallery#loadTextureFile(File)}
* @see TextureGallery#loadTextureFile(File)
*/
public void loadTextureFile(File file) throws IOException, ParseResourceException {
textures.loadTextureFile(file);
}
/**
* See {@link TextureGallery#saveTextureFile(File)}
* @see TextureGallery#saveTextureFile(File)
*/
public void saveTextureFile(File file) throws IOException {
textures.saveTextureFile(file);
} }
/** /**
@ -159,37 +140,61 @@ public class ResourcePack {
String filename = FileAccess.getFileName(blockstateFile); String filename = FileAccess.getFileName(blockstateFile);
if (!filename.endsWith(".json")) continue; if (!filename.endsWith(".json")) continue;
String jsonFileName = filename.substring(0, filename.length() - 5);
try { try {
blockStateResources.put(namespace + ":" + filename.substring(0, filename.length() - 5), builder.build(blockstateFile)); blockStateResources.put(namespace + ":" + jsonFileName, builder.build(blockstateFile));
} catch (IOException ex) { } catch (IOException ex) {
Logger.global.logError("Failed to load blockstate: " + namespace + ":" + filename.substring(0, filename.length() - 5), ex); Logger.global.logError("Failed to load blockstate: " + namespace + ":" + jsonFileName, ex);
} }
} }
//load configs //load biomes
for (String configName : CONFIG_FILES) { try {
try { GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
Resource config = new Resource(sourcesAccess.readFile( .source(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(
"assets/" + namespace + "/" + configName)); "assets/" + namespace + "/biomes.json"))))
configs.computeIfAbsent(configName, t -> new ArrayList<>()).add(config); .build();
} catch (FileNotFoundException ignore) { biomeConfig.load(loader.load());
} catch (IOException ex) { } catch (IOException ex) {
Logger.global.logError("Failed to load config for " + namespace + ": " + configName, ex); Logger.global.logError("Failed to load biomes.conf from: " + namespace, ex);
} }
//load block properties
try {
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
.source(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(
"assets/" + namespace + "/blockProperties.json"))))
.build();
blockPropertiesConfig.load(loader.load());
} catch (IOException ex) {
Logger.global.logError("Failed to load biomes.conf from: " + namespace, ex);
}
//load block colors
try {
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
.source(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(
"assets/" + namespace + "/blockColors.json"))))
.build();
blockColorCalculatorFactory.load(loader.load());
} catch (IOException ex) {
Logger.global.logError("Failed to load biomes.conf from: " + namespace, ex);
} }
} }
try { try {
foliageMap = ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/foliage.png")); blockColorCalculatorFactory.setFoliageMap(
blockColorCalculatorFactory.setFoliageMap(foliageMap); ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/foliage.png"))
} catch (IOException ex) { );
} catch (IOException | ArrayIndexOutOfBoundsException ex) {
Logger.global.logError("Failed to load foliagemap!", ex); Logger.global.logError("Failed to load foliagemap!", ex);
} }
try { try {
grassMap = ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/grass.png")); blockColorCalculatorFactory.setGrassMap(
blockColorCalculatorFactory.setGrassMap(grassMap); ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/grass.png"))
} catch (IOException ex) { );
} catch (IOException | ArrayIndexOutOfBoundsException ex) {
Logger.global.logError("Failed to load grassmap!", ex); Logger.global.logError("Failed to load grassmap!", ex);
} }
@ -197,6 +202,22 @@ public class ResourcePack {
Logger.global.logError("Failed to close FileAccess!", ex); Logger.global.logError("Failed to close FileAccess!", ex);
} }
} }
/**
* See {@link TextureGallery#loadTextureFile(File)}
* @see TextureGallery#loadTextureFile(File)
*/
public void loadTextureFile(File file) throws IOException, ParseResourceException {
textures.loadTextureFile(file);
}
/**
* See {@link TextureGallery#saveTextureFile(File)}
* @see TextureGallery#saveTextureFile(File)
*/
public void saveTextureFile(File file) throws IOException {
textures.saveTextureFile(file);
}
/** /**
* Returns a {@link BlockStateResource} for the given {@link BlockState} if found. * Returns a {@link BlockStateResource} for the given {@link BlockState} if found.
@ -209,16 +230,48 @@ public class ResourcePack {
if (resource == null) throw new NoSuchResourceException("No resource for blockstate: " + state.getFullId()); if (resource == null) throw new NoSuchResourceException("No resource for blockstate: " + state.getFullId());
return resource; return resource;
} }
public BlockProperties getBlockProperties(BlockState state) {
return blockPropertiesCache.get(state);
}
private BlockProperties getBlockPropertiesNoCache(BlockState state) {
BlockProperties.Builder props = blockPropertiesConfig.getBlockProperties(state).toBuilder();
if (props.isOccluding() == Tristate.UNDEFINED || props.isCulling() == Tristate.UNDEFINED) {
try {
BlockStateResource resource = getBlockStateResource(state);
for (TransformedBlockModelResource bmr : resource.getModels(state, new ArrayList<>())) {
if (props.isOccluding() == Tristate.UNDEFINED) props.occluding(bmr.getModel().isOccluding());
if (props.isCulling() == Tristate.UNDEFINED) props.culling(bmr.getModel().isCulling());
}
} catch (NoSuchResourceException ignore) {}
}
return props.build();
}
public Biome getBiome(int id) {
return biomeConfig.getBiome(id);
}
public Map<String, BlockStateResource> getBlockStateResources() {
return blockStateResources;
}
public Map<String, BlockModelResource> getBlockModelResources() {
return blockModelResources;
}
public TextureGallery getTextures() {
return textures;
}
public BlockColorCalculatorFactory getBlockColorCalculatorFactory() { public BlockColorCalculatorFactory getBlockColorCalculatorFactory() {
return blockColorCalculatorFactory; return blockColorCalculatorFactory;
} }
public MinecraftVersion getMinecraftVersion() { public static String namespacedToAbsoluteResourcePath(String namespacedPath, String resourceTypeFolder) {
return minecraftVersion;
}
protected static String namespacedToAbsoluteResourcePath(String namespacedPath, String resourceTypeFolder) {
String path = namespacedPath; String path = namespacedPath;
resourceTypeFolder = FileAccess.normalize(resourceTypeFolder); resourceTypeFolder = FileAccess.normalize(resourceTypeFolder);
@ -246,7 +299,7 @@ public class ResourcePack {
private final byte[] data; private final byte[] data;
public Resource(InputStream data) throws FileNotFoundException, IOException { public Resource(InputStream data) throws IOException {
try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) { try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
bout.write(data); bout.write(data);
this.data = bout.toByteArray(); this.data = bout.toByteArray();

View File

@ -22,13 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack.blockmodel;
import com.flowpowered.math.TrigMath; import com.flowpowered.math.TrigMath;
import com.flowpowered.math.vector.Vector3f; import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f; import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource.Element.Face; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource.Element.Face;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.util.Direction; import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.util.math.Axis; import de.bluecolored.bluemap.core.util.math.Axis;
@ -45,13 +48,13 @@ public class BlockModelResource {
private static final double FIT_TO_BLOCK_SCALE_MULTIPLIER = 2 - Math.sqrt(2); private static final double FIT_TO_BLOCK_SCALE_MULTIPLIER = 2 - Math.sqrt(2);
private ModelType modelType = ModelType.NORMAL; private ModelType modelType = ModelType.NORMAL;
private boolean culling = false; private boolean culling = false;
private boolean occluding = false; private boolean occluding = false;
private boolean ambientOcclusion = true; private final boolean ambientOcclusion = true; //TODO: wat?
private Collection<Element> elements = new ArrayList<>(); private final Collection<Element> elements = new ArrayList<>();
private Map<String, Texture> textures = new HashMap<>(); private final Map<String, Texture> textures = new HashMap<>();
private BlockModelResource() {} private BlockModelResource() {}
@ -477,9 +480,9 @@ public class BlockModelResource {
Texture texture; Texture texture;
try { try {
texture = resourcePack.textures.get(path); texture = resourcePack.getTextures().get(path);
} catch (NoSuchElementException ex) { } catch (NoSuchElementException ex) {
texture = resourcePack.textures.loadTexture(sourcesAccess, path); texture = resourcePack.getTextures().loadTexture(sourcesAccess, path);
} }
return texture; return texture;

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack.blockmodel;
public enum ModelType { public enum ModelType {

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack.blockmodel;
import com.flowpowered.math.vector.Vector2f; import com.flowpowered.math.vector.Vector2f;
import de.bluecolored.bluemap.core.util.math.MatrixM3f; import de.bluecolored.bluemap.core.util.math.MatrixM3f;

View File

@ -22,19 +22,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack.blockstate;
import com.flowpowered.math.vector.Vector2f; import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.PropertyCondition.All; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockstate.PropertyCondition.All;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.util.MathUtils;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import org.apache.commons.lang3.StringUtils;
import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.gson.GsonConfigurationLoader; import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@ -46,8 +47,6 @@ import java.util.Map.Entry;
public class BlockStateResource { public class BlockStateResource {
private static final MinecraftVersion NEW_MODEL_PATH_VERSION = new MinecraftVersion(1, 13);
private final List<Variant> variants = new ArrayList<>(0); private final List<Variant> variants = new ArrayList<>(0);
private final Collection<Variant> multipart = new ArrayList<>(0); private final Collection<Variant> multipart = new ArrayList<>(0);
@ -92,53 +91,6 @@ public class BlockStateResource {
return targetCollection; return targetCollection;
} }
private static class Variant {
private PropertyCondition condition = PropertyCondition.all();
private Collection<Weighted<TransformedBlockModelResource>> models = new ArrayList<>();
private double totalWeight;
private Variant() {
}
public TransformedBlockModelResource getModel(int x, int y, int z) {
if (models.isEmpty()) throw new IllegalStateException("A variant must have at least one model!");
double selection = MathUtils.hashToFloat(x, y, z, 827364) * totalWeight; // random based on position
for (Weighted<TransformedBlockModelResource> w : models) {
selection -= w.weight;
if (selection <= 0) return w.value;
}
throw new RuntimeException("This line should never be reached!");
}
public void checkValid() throws ParseResourceException {
if (models.isEmpty()) throw new ParseResourceException("A variant must have at least one model!");
}
public void updateTotalWeight() {
totalWeight = 0d;
for (Weighted<?> w : models) {
totalWeight += w.weight;
}
}
}
private static class Weighted<T> {
private final T value;
private final double weight;
public Weighted(T value, double weight) {
this.value = value;
this.weight = weight;
}
}
public static Builder builder(FileAccess sourcesAccess, ResourcePack resourcePack) { public static Builder builder(FileAccess sourcesAccess, ResourcePack resourcePack) {
return new Builder(sourcesAccess, resourcePack); return new Builder(sourcesAccess, resourcePack);
} }
@ -241,16 +193,9 @@ public class BlockStateResource {
String namespacedModelPath = node.node("model").getString(); String namespacedModelPath = node.node("model").getString();
if (namespacedModelPath == null) if (namespacedModelPath == null)
throw new ParseResourceException("No model defined!"); throw new ParseResourceException("No model defined!");
String modelPath;
if (resourcePack.getMinecraftVersion().isBefore(NEW_MODEL_PATH_VERSION)) {
modelPath = ResourcePack.namespacedToAbsoluteResourcePath(namespacedModelPath, "models/block") + ".json";
}else {
modelPath = ResourcePack.namespacedToAbsoluteResourcePath(namespacedModelPath, "models") + ".json";
}
BlockModelResource model = resourcePack.blockModelResources.get(modelPath); String modelPath = ResourcePack.namespacedToAbsoluteResourcePath(namespacedModelPath, "models") + ".json";
BlockModelResource model = resourcePack.getBlockModelResources().get(modelPath);
if (model == null) { if (model == null) {
BlockModelResource.Builder builder = BlockModelResource.builder(sourcesAccess, resourcePack); BlockModelResource.Builder builder = BlockModelResource.builder(sourcesAccess, resourcePack);
try { try {
@ -260,7 +205,7 @@ public class BlockStateResource {
throw new ParseResourceException("Failed to load model " + modelPath, e); throw new ParseResourceException("Failed to load model " + modelPath, e);
} }
resourcePack.blockModelResources.put(modelPath, model); resourcePack.getBlockModelResources().put(modelPath, model);
} }
Vector2f rotation = new Vector2f(node.node("x").getFloat(0), node.node("y").getFloat(0)); Vector2f rotation = new Vector2f(node.node("x").getFloat(0), node.node("y").getFloat(0));

View File

@ -22,21 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.mca.extensions; package de.bluecolored.bluemap.core.resourcepack.blockstate;
import java.util.Collections; public class Conditional<T> {
import java.util.HashSet;
import java.util.Set;
public class TripwireConnectExtension extends ConnectExtension { private final PropertyCondition condition;
private final T value;
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList( public Conditional(PropertyCondition condition, T value) {
"minecraft:tripwire" this.condition = condition;
)); this.value = value;
}
@Override
public Set<String> getAffectedBlockIds() { public PropertyCondition getCondition() {
return AFFECTED_BLOCK_IDS; return condition;
} }
public T getValue() {
return value;
}
} }

View File

@ -22,12 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack.blockstate;
import de.bluecolored.bluemap.core.util.Preconditions; import de.bluecolored.bluemap.core.util.Preconditions;
import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Map;
@FunctionalInterface @FunctionalInterface
public interface PropertyCondition { public interface PropertyCondition {
@ -35,7 +37,7 @@ public interface PropertyCondition {
PropertyCondition MATCH_NONE = new None(); PropertyCondition MATCH_NONE = new None();
boolean matches(BlockState state); boolean matches(BlockState state);
class Property implements PropertyCondition { class Property implements PropertyCondition {
private final String key; private final String key;
@ -151,5 +153,18 @@ public interface PropertyCondition {
return or(conditions); return or(conditions);
} }
static PropertyCondition blockState(BlockState state) {
Map<String, String> props = state.getProperties();
if (props.isEmpty()) return all();
PropertyCondition[] conditions = new Property[props.size()];
int i = 0;
for (Map.Entry<String, String> prop : props.entrySet()) {
conditions[i++] = property(prop.getKey(), prop.getValue());
}
return and(conditions);
}
} }

View File

@ -0,0 +1,70 @@
/*
* 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.resourcepack.blockstate;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import java.util.ArrayList;
import java.util.Collection;
public class Variant {
PropertyCondition condition = PropertyCondition.all();
Collection<Weighted<TransformedBlockModelResource>> models = new ArrayList<>(0);
private double totalWeight;
Variant() {}
public TransformedBlockModelResource getModel(int x, int y, int z) {
if (models.isEmpty()) throw new IllegalStateException("A variant must have at least one model!");
double selection = hashToFloat(x, y, z) * totalWeight; // random based on position
for (Weighted<TransformedBlockModelResource> w : models) {
selection -= w.getWeight();
if (selection <= 0) return w.getValue();
}
throw new RuntimeException("This line should never be reached!");
}
public void checkValid() throws ParseResourceException {
if (models.isEmpty()) throw new ParseResourceException("A variant must have at least one model!");
}
public void updateTotalWeight() {
totalWeight = 0d;
for (Weighted<?> w : models) {
totalWeight += w.getWeight();
}
}
private static float hashToFloat(int x, int y, int z) {
final long hash = x * 73438747 ^ y * 9357269 ^ z * 4335792;
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
}
}

View File

@ -22,19 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.mca.extensions; package de.bluecolored.bluemap.core.resourcepack.blockstate;
import java.util.Set; public class Weighted<T> {
import com.flowpowered.math.vector.Vector3i; private final T value;
private final double weight;
import de.bluecolored.bluemap.core.mca.MCAWorld; public Weighted(T value, double weight) {
import de.bluecolored.bluemap.core.world.BlockState; this.value = value;
this.weight = weight;
}
public interface BlockStateExtension { public double getWeight() {
return weight;
}
public T getValue() {
return value;
}
BlockState extend(MCAWorld world, Vector3i pos, BlockState state);
Set<String> getAffectedBlockIds();
} }

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack.texture;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;

View File

@ -22,10 +22,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.resourcepack; package de.bluecolored.bluemap.core.resourcepack.texture;
import com.google.gson.*; import com.google.gson.*;
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess; import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.util.FileUtils; import de.bluecolored.bluemap.core.util.FileUtils;
import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.Color;
@ -37,14 +38,14 @@ import java.util.*;
/** /**
* A {@link TextureGallery} is managing {@link Texture}s and their id's and path's.<br> * A {@link TextureGallery} is managing {@link Texture}s and their id's and path's.<br>
* I can also load and generate the texture.json file, or load new {@link Texture}s from a {@link FileAccess}. * I can also load and generate the texture.json file, or load new {@link Texture}s from a {@link FileAccess}.
*/ */
public class TextureGallery { public class TextureGallery {
private static final String EMPTY_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAEUlEQVR42mNkIAAYRxWMJAUAE5gAEdz4t9QAAAAASUVORK5CYII="; private static final String EMPTY_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAEUlEQVR42mNkIAAYRxWMJAUAE5gAEdz4t9QAAAAASUVORK5CYII=";
private Map<String, Texture> textureMap; private final Map<String, Texture> textureMap;
private List<Texture> textureList; private final List<Texture> textureList;
public TextureGallery() { public TextureGallery() {
textureMap = new HashMap<>(); textureMap = new HashMap<>();
@ -129,7 +130,7 @@ public class TextureGallery {
* Loads all the {@link Texture}s from the provided texture.json file, removes any existing {@link Texture}s from this gallery. * Loads all the {@link Texture}s from the provided texture.json file, removes any existing {@link Texture}s from this gallery.
* @param file The texture.json file. * @param file The texture.json file.
* @throws IOException If an IOException occurs while reading the file. * @throws IOException If an IOException occurs while reading the file.
* @throws ParseResourceException If the whole file can not be read. Errors with single textures are logged and ignored. * @throws ParseResourceException If the whole file can not be read. Errors with single textures are logged and ignored.
*/ */
public synchronized void loadTextureFile(File file) throws IOException, ParseResourceException { public synchronized void loadTextureFile(File file) throws IOException, ParseResourceException {
textureList.clear(); textureList.clear();
@ -149,13 +150,11 @@ public class TextureGallery {
String path = texture.get("id").getAsString(); String path = texture.get("id").getAsString();
boolean transparent = texture.get("transparent").getAsBoolean(); boolean transparent = texture.get("transparent").getAsBoolean();
Color color = readColor(texture.get("color").getAsJsonArray()); Color color = readColor(texture.get("color").getAsJsonArray());
textureList.set(i, new Texture(i, path, color, transparent, EMPTY_BASE64)); textureList.set(i, new Texture(i, path, color, transparent, texture.get("texture").getAsString()));
} catch (ParseResourceException | RuntimeException ex) { } catch (ParseResourceException | RuntimeException ex) {
Logger.global.logWarning("Failed to load texture with id " + i + " from texture file " + file + "!"); Logger.global.logWarning("Failed to load texture with id " + i + " from texture file " + file + "!");
} }
} }
} catch (IOException ex) {
throw ex;
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw new ParseResourceException("Invalid texture file format!", ex); throw new ParseResourceException("Invalid texture file format!", ex);
} finally { } finally {
@ -210,7 +209,7 @@ public class TextureGallery {
return texture; return texture;
} }
} }
/** /**
* Tries to reload all {@link Texture}s from the given {@link FileAccess}<br> * Tries to reload all {@link Texture}s from the given {@link FileAccess}<br>
* <br> * <br>
@ -221,11 +220,7 @@ public class TextureGallery {
for (Texture texture : textureList.toArray(new Texture[textureList.size()])) { for (Texture texture : textureList.toArray(new Texture[textureList.size()])) {
try { try {
loadTexture(fileAccess, texture.getPath()); loadTexture(fileAccess, texture.getPath());
} catch (IOException e) { } catch (IOException ignored) {}
Logger.global.noFloodWarning("TextureGallery-uiz78tef5", "Failed to reload texture: " + texture.getPath());
Logger.global.noFloodWarning("TextureGallery-89763455h", "This happens if the resource-packs have changed, but you have not deleted your already generated maps. This might result in broken map-models!");
Logger.global.noFloodWarning("TextureGallery-re56ugb56", "(Future warnings of this will be suppressed, so more textures might have failed to load after this)");
}
} }
} }

View File

@ -1,59 +0,0 @@
package de.bluecolored.bluemap.core.util;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* A pool of objects that (lazily) maintains a specific size of objects.
* that threads can take, use and return.
* It discards excessive objects and creates new ones when needed.
*/
public class ArrayPool<T> {
private final Object[] objects;
private final int capacity, maxConcurrency;
private final Supplier<T> supplier;
private final AtomicInteger head, tail; // head is excluded, tail included
private int size;
public ArrayPool(int capacity, int maxConcurrency, Supplier<T> supplier) {
this.capacity = capacity;
this.objects = new Object[capacity + maxConcurrency * 2];
this.maxConcurrency = maxConcurrency;
this.supplier = supplier;
this.head = new AtomicInteger(0);
this.tail = new AtomicInteger(0);
this.size = 0;
}
@SuppressWarnings("unchecked")
public T take() {
while (size < maxConcurrency) add(supplier.get());
size --;
return (T) objects[tail.getAndUpdate(this::nextPointer)];
}
public void add(T resource) {
while (size > capacity + maxConcurrency) take();
objects[head.getAndUpdate(this::nextPointer)] = resource;
size ++;
}
public int size() {
return size;
}
public int capacity() {
return capacity;
}
private int nextPointer(int prev) {
return (prev + 1) % objects.length;
}
}

View File

@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d;
import com.flowpowered.math.vector.Vector3f; import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f; import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.util.math.VectorM3f;
@Deprecated //TODO @Deprecated //TODO
public class MathUtils { public class MathUtils {

View File

@ -22,48 +22,51 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.sponge; package de.bluecolored.bluemap.core.util;
import org.slf4j.Logger; import java.util.function.BooleanSupplier;
import de.bluecolored.bluemap.core.logger.AbstractLogger; public enum Tristate {
public class Slf4jLogger extends AbstractLogger { TRUE (true),
UNDEFINED (false) {
@Override
public Tristate getOr(Tristate other) {
return other;
}
private Logger out; @Override
public boolean getOr(BooleanSupplier other) {
public Slf4jLogger(Logger out) { return other.getAsBoolean();
this.out = out; }
}
@Override @Override
public void logError(String message, Throwable throwable) { public boolean getOr(boolean defaultValue) {
out.error(message, throwable); return defaultValue;
} }
},
FALSE (false);
@Override private final boolean value;
public void logWarning(String message) {
out.warn(message);
}
@Override Tristate(boolean value ) {
public void logInfo(String message) { this.value = value;
out.info(message); }
}
@Override public Tristate getOr(Tristate other) {
public void logDebug(String message) { return this;
if (out.isDebugEnabled()) out.debug(message); }
}
public boolean getOr(BooleanSupplier other) {
@Override return value;
public void noFloodDebug(String message) { }
if (out.isDebugEnabled()) super.noFloodDebug(message);
} public boolean getOr(boolean defaultValue) {
return value;
@Override }
public void noFloodDebug(String key, String message) {
if (out.isDebugEnabled()) super.noFloodDebug(key, message); @Override
} public String toString() {
return "Tristate." + name();
}
} }

View File

@ -1,3 +1,27 @@
/*
* 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.util.math; package de.bluecolored.bluemap.core.util.math;
public class Color { public class Color {

View File

@ -24,10 +24,7 @@
*/ */
package de.bluecolored.bluemap.core.world; package de.bluecolored.bluemap.core.world;
import com.flowpowered.math.vector.Vector3i; public class Block<T extends Block<T>> {
import de.bluecolored.bluemap.core.util.Direction;
public class Block {
private World world; private World world;
private int x, y, z; private int x, y, z;
@ -35,23 +32,19 @@ public class Block {
private Chunk chunk; private Chunk chunk;
private BlockState blockState; private BlockState blockState;
private BlockProperties properties;
private LightData lightData; private LightData lightData;
private Biome biome; private int biomeId;
private int sunLight;
private int blockLight;
private final transient LightData tempLight;
public Block(World world, int x, int y, int z) { public Block(World world, int x, int y, int z) {
tempLight = new LightData(0, 0);
set(world, x, y, z); set(world, x, y, z);
} }
public Block set(World world, int x, int y, int z) { public T set(World world, int x, int y, int z) {
if (this.x == x && this.y == y && this.z == z && this.world == world) return this; if (this.x == x && this.z == z && this.world == world){
if (this.y == y) return self();
} else {
this.chunk = null; //only reset the chunk if x or z have changed
}
this.world = world; this.world = world;
this.x = x; this.x = x;
@ -60,11 +53,15 @@ public class Block {
reset(); reset();
return this; return self();
} }
public Block set(int x, int y, int z) { public T set(int x, int y, int z) {
if (this.x == x && this.y == y && this.z == z) return this; if (this.x == x && this.z == z){
if (this.y == y) return self();
} else {
this.chunk = null; //only reset the chunk if x or z have changed
}
this.x = x; this.x = x;
this.y = y; this.y = y;
@ -72,27 +69,49 @@ public class Block {
reset(); reset();
return this; return self();
} }
private void reset() { protected void reset() {
this.chunk = null;
this.blockState = null; this.blockState = null;
this.properties = null;
this.lightData = new LightData(-1, -1); this.lightData = new LightData(-1, -1);
this.biome = null; this.biomeId = -1;
this.blockLight = -1;
this.sunLight = -1;
} }
public Block add(int dx, int dy, int dz) { public T add(int dx, int dy, int dz) {
return set(x + dx, y + dy, z + dz); return set(x + dx, y + dy, z + dz);
} }
public Block copy(Block source) { public T copy(Block<?> source) {
return set(source.world, source.x, source.y, source.z); this.world = source.world;
this.chunk = source.chunk;
this.x = source.x;
this.y = source.y;
this.z = source.z;
reset();
this.blockState = source.blockState;
this.lightData = new LightData(source.lightData.getSkyLight(), source.lightData.getBlockLight());
this.biomeId = source.biomeId;
return self();
}
/**
* copy with offset
*/
public T copy(Block<?> source, int dx, int dy, int dz) {
this.world = source.world;
this.x = source.x + dx;
this.y = source.y + dy;
this.z = source.z + dz;
this.chunk = null;
reset();
return self();
} }
public World getWorld() { public World getWorld() {
@ -121,19 +140,14 @@ public class Block {
return blockState; return blockState;
} }
public BlockProperties getProperties() {
if (properties == null) properties = world.getBlockProperties(getBlockState());
return properties;
}
public LightData getLightData() { public LightData getLightData() {
if (lightData.getSkyLight() < 0) getChunk().getLightData(x, y, z, lightData); if (lightData.getSkyLight() < 0) getChunk().getLightData(x, y, z, lightData);
return lightData; return lightData;
} }
public Biome getBiome() { public int getBiomeId() {
if (biome == null) biome = getChunk().getBiome(x, y, z); if (biomeId == -1) biomeId = getChunk().getBiome(x, y, z);
return biome; return biomeId;
} }
public int getSunLightLevel() { public int getSunLightLevel() {
@ -144,67 +158,32 @@ public class Block {
return getLightData().getBlockLight(); return getLightData().getBlockLight();
} }
public boolean isCullingNeighborFaces() { @Override
return getProperties().isCulling(); public String toString() {
} if (world != null) {
return "Block{" +
public boolean isFlammable() { "world=" + world +
return getProperties().isFlammable(); ", x=" + x +
} ", y=" + y +
", z=" + z +
public boolean isOccludingNeighborFaces(){ ", chunk=" + getChunk() +
return getProperties().isOccluding(); ", blockState=" + getBlockState() +
} ", lightData=" + getLightData() +
", biomeId=" + getBiomeId() +
/** '}';
* This is internally used for light rendering } else {
* It is basically the sun light that is projected onto adjacent faces return "Block{" +
*/ "world=" + world +
public int getPassedSunLight() { ", x=" + x +
if (sunLight < 0) calculateLight(); ", y=" + y +
return sunLight; ", z=" + z +
} '}';
/**
* This is internally used for light rendering
* It is basically the block light that is projected onto adjacent faces
*/
public int getPassedBlockLight() {
if (blockLight < 0) calculateLight();
return blockLight;
}
private void calculateLight() {
sunLight = getSunLightLevel();
blockLight = getBlockLightLevel();
if (blockLight > 0 || sunLight > 0) return;
Vector3i dirV;
int nx, ny, nz;
for (Direction direction : Direction.values()) {
dirV = direction.toVector();
nx = dirV.getX() + x;
ny = dirV.getY() + y;
nz = dirV.getZ() + z;
world.getLightData(nx, ny, nz, tempLight);
sunLight = Math.max(tempLight.getSkyLight(), sunLight);
blockLight = Math.max(tempLight.getBlockLight(), blockLight);
} }
} }
@Override @SuppressWarnings("unchecked")
public String toString() { protected T self() {
return "Block{" + return (T) this;
"world=" + world +
", x=" + x +
", y=" + y +
", z=" + z +
", sunLight=" + sunLight +
", blockLight=" + blockLight +
'}';
} }
} }

View File

@ -0,0 +1,90 @@
/*
* 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.world;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
public class BlockNeighborhood<T extends BlockNeighborhood<T>> extends ResourcePackBlock<T> {
private static final int DIAMETER = 8;
private static final int DIAMETER_MASK = DIAMETER - 1;
private static final int DIAMETER_SQUARED = DIAMETER * DIAMETER;
private final ResourcePackBlock<?>[] neighborhood;
private int thisIndex;
public BlockNeighborhood(ResourcePackBlock<?> center) {
super(center.getResourcePack(), null, 0, 0, 0);
copy(center);
neighborhood = new ResourcePackBlock[DIAMETER * DIAMETER * DIAMETER];
init();
}
public BlockNeighborhood(ResourcePack resourcePack, World world, int x, int y, int z) {
super(resourcePack, world, x, y, z);
neighborhood = new ResourcePackBlock[DIAMETER * DIAMETER * DIAMETER];
init();
}
@Override
protected void reset() {
super.reset();
this.thisIndex = -1;
}
private void init() {
this.thisIndex = -1;
for (int i = 0; i < neighborhood.length; i++) {
neighborhood[i] = new ResourcePackBlock<>(this.getResourcePack(), null, 0, 0, 0);
}
}
public ResourcePackBlock<?> getNeighborBlock(int dx, int dy, int dz) {
int i = neighborIndex(dx, dy, dz);
if (i == thisIndex()) return this;
return neighborhood[i].set(
getWorld(),
getX() + dx,
getY() + dy,
getZ() + dz
);
}
private int thisIndex() {
if (thisIndex == -1) thisIndex = neighborIndex(0, 0, 0);
return thisIndex;
}
private int neighborIndex(int dx, int dy, int dz) {
return ((getX() + dx) & DIAMETER_MASK) * DIAMETER_SQUARED +
((getY() + dy) & DIAMETER_MASK) * DIAMETER +
((getZ() + dz) & DIAMETER_MASK);
}
}

View File

@ -24,29 +24,122 @@
*/ */
package de.bluecolored.bluemap.core.world; package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.core.util.Tristate;
public class BlockProperties { public class BlockProperties {
public static final BlockProperties SOLID = new BlockProperties(true, true, false); public static final BlockProperties DEFAULT = new BlockProperties();
public static final BlockProperties TRANSPARENT = new BlockProperties(false, false, false);
private final boolean culling, occluding, flammable; private Tristate culling, occluding, alwaysWaterlogged, randomOffset;
public BlockProperties(boolean culling, boolean occluding, boolean flammable) { public BlockProperties() {
this.culling = Tristate.UNDEFINED;
this.occluding = Tristate.UNDEFINED;
this.alwaysWaterlogged = Tristate.UNDEFINED;
this.randomOffset = Tristate.UNDEFINED;
}
public BlockProperties(
Tristate culling,
Tristate occluding,
Tristate alwaysWaterlogged,
Tristate randomOffset
) {
this.culling = culling; this.culling = culling;
this.occluding = occluding; this.occluding = occluding;
this.flammable = flammable; this.alwaysWaterlogged = alwaysWaterlogged;
this.randomOffset = randomOffset;
} }
public boolean isCulling() { public boolean isCulling() {
return culling; return culling.getOr(true);
} }
public boolean isOccluding() { public boolean isOccluding() {
return occluding; return occluding.getOr(true);
} }
public boolean isFlammable() { public boolean isAlwaysWaterlogged() {
return flammable; return alwaysWaterlogged.getOr(false);
} }
public boolean isRandomOffset() {
return randomOffset.getOr(false);
}
public Builder toBuilder() {
return new BlockProperties(
culling,
occluding,
alwaysWaterlogged,
randomOffset
).new Builder();
}
public static Builder builder() {
return new BlockProperties().new Builder();
}
public class Builder {
public Builder culling(boolean culling) {
BlockProperties.this.culling = culling ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder occluding(boolean occluding) {
BlockProperties.this.occluding = occluding ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder alwaysWaterlogged(boolean alwaysWaterlogged) {
BlockProperties.this.alwaysWaterlogged = alwaysWaterlogged ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder randomOffset(boolean randomOffset) {
BlockProperties.this.randomOffset = randomOffset ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder from(BlockProperties other) {
culling = other.culling.getOr(culling);
occluding = other.occluding.getOr(occluding);
alwaysWaterlogged = other.alwaysWaterlogged.getOr(alwaysWaterlogged);
randomOffset = other.randomOffset.getOr(randomOffset);
return this;
}
public BlockProperties build() {
return BlockProperties.this;
}
public Tristate isCulling() {
return culling;
}
public Tristate isOccluding() {
return occluding;
}
public Tristate isAlwaysWaterlogged() {
return alwaysWaterlogged;
}
public Tristate isRandomOffset() {
return randomOffset;
}
}
@Override
public String toString() {
return "BlockProperties{" +
"culling=" + culling +
", occluding=" + occluding +
", alwaysWaterlogged=" + alwaysWaterlogged +
", randomOffset=" + randomOffset +
'}';
}
} }

View File

@ -24,8 +24,6 @@
*/ */
package de.bluecolored.bluemap.core.world; package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.core.MinecraftVersion;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -39,46 +37,10 @@ import java.util.regex.Pattern;
*/ */
public class BlockState { public class BlockState {
private static final Pattern BLOCKSTATE_SERIALIZATION_PATTERN = Pattern.compile("^(.+?)(?:\\[(.*)\\])?$"); private static final Pattern BLOCKSTATE_SERIALIZATION_PATTERN = Pattern.compile("^(.+?)(?:\\[(.*)])?$");
private static final HashSet<String> DEFAULT_WATERLOGGED_BLOCK_IDS = new HashSet<>(Arrays.asList( public static final BlockState AIR = new BlockState("minecraft:air");
"minecraft:seagrass", public static final BlockState MISSING = new BlockState("bluemap:missing");
"minecraft:tall_seagrass",
"minecraft:kelp",
"minecraft:kelp_plant",
"minecraft:bubble_column"
));
private static final HashSet<String> OFFSET_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:grass",
"minecraft:tall_grass",
"minecraft:fern",
"minecraft:dandelion",
"minecraft:cornflower",
"minecraft:poppy",
"minecraft:blue_orchid",
"minecraft:allium",
"minecraft:azure_bluet",
"minecraft:red_tulip",
"minecraft:orange_tulip",
"minecraft:white_tulip",
"minecraft:pink_tulip",
"minecraft:oxeye_daisy",
"minecraft:lily_of_the_valley",
"minecraft:wither_rose",
"minecraft:crimson_roots",
"minecraft:warped_roots",
"minecraft:nether_sprouts",
"minecraft:rose_bush",
"minecraft:peony",
"minecraft:lilac",
"minecraft:sunflower",
"minecraft:hanging_roots",
"minecraft:small_dripleaf"
));
public static final BlockState AIR = new BlockState(MinecraftVersion.LATEST_SUPPORTED, "minecraft:air", Collections.emptyMap());
public static final BlockState MISSING = new BlockState(MinecraftVersion.LATEST_SUPPORTED, "bluemap:missing", Collections.emptyMap());
private boolean hashed; private boolean hashed;
private int hash; private int hash;
@ -88,14 +50,13 @@ public class BlockState {
private final String fullId; private final String fullId;
private final Map<String, String> properties; private final Map<String, String> properties;
// special fast-access properties private final boolean isAir, isWater, isWaterlogged;
public final boolean isAir, isWater, isWaterlogged, isRandomOffset;
public BlockState(MinecraftVersion version, String id) { public BlockState(String id) {
this(version, id, Collections.emptyMap()); this(id, Collections.emptyMap());
} }
public BlockState(MinecraftVersion version, String id, Map<String, String> properties) { public BlockState(String id, Map<String, String> properties) {
this.hashed = false; this.hashed = false;
this.hash = 0; this.hash = 0;
@ -121,38 +82,7 @@ public class BlockState {
"minecraft:void_air".equals(this.fullId); "minecraft:void_air".equals(this.fullId);
this.isWater = "minecraft:water".equals(this.fullId); this.isWater = "minecraft:water".equals(this.fullId);
this.isWaterlogged = "true".equals(properties.get("waterlogged"));
this.isWaterlogged =
DEFAULT_WATERLOGGED_BLOCK_IDS.contains(this.fullId) ||
"true".equals(this.properties.get("waterlogged"));
if (version.isAtLeast(MinecraftVersion.THE_FLATTENING)) {
this.isRandomOffset = OFFSET_BLOCK_IDS.contains(this.fullId);
} else {
this.isRandomOffset =
"minecraft:tall_grass".equals(this.fullId);
}
}
private BlockState(BlockState blockState, String withKey, String withValue) {
this.hashed = false;
this.hash = 0;
Map<String, String> props = new HashMap<>(blockState.getProperties());
props.put(withKey, withValue);
this.id = blockState.getId();
this.namespace = blockState.getNamespace();
this.fullId = namespace + ":" + id;
this.properties = props;
// special fast-access properties
this.isAir = blockState.isAir;
this.isWater = blockState.isWater;
this.isWaterlogged =
DEFAULT_WATERLOGGED_BLOCK_IDS.contains(this.fullId) ||
"true".equals(this.properties.get("waterlogged"));
this.isRandomOffset = blockState.isRandomOffset;
} }
/** /**
@ -190,14 +120,19 @@ public class BlockState {
public Map<String, String> getProperties() { public Map<String, String> getProperties() {
return properties; return properties;
} }
/** public boolean isAir() {
* Returns a new BlockState with the given property changed return isAir;
*/
public BlockState with(String property, String value) {
return new BlockState(this, property, value);
} }
public boolean isWater() {
return isWater;
}
public boolean isWaterlogged() {
return isWaterlogged;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) return true; if (this == obj) return true;
@ -205,8 +140,7 @@ public class BlockState {
if (!(obj instanceof BlockState)) return false; if (!(obj instanceof BlockState)) return false;
BlockState b = (BlockState) obj; BlockState b = (BlockState) obj;
if (!Objects.equals(getFullId(), b.getFullId())) return false; if (!Objects.equals(getFullId(), b.getFullId())) return false;
if (!Objects.equals(getProperties(), b.getProperties())) return false; return Objects.equals(getProperties(), b.getProperties());
return true;
} }
@Override @Override
@ -229,10 +163,12 @@ public class BlockState {
return getFullId() + "[" + sj.toString() + "]"; return getFullId() + "[" + sj.toString() + "]";
} }
public static BlockState fromString(MinecraftVersion version, String serializedBlockState) throws IllegalArgumentException { public static BlockState fromString(String serializedBlockState) throws IllegalArgumentException {
try { try {
Matcher m = BLOCKSTATE_SERIALIZATION_PATTERN.matcher(serializedBlockState); Matcher m = BLOCKSTATE_SERIALIZATION_PATTERN.matcher(serializedBlockState);
m.find();
if (!m.find())
throw new IllegalArgumentException("'" + serializedBlockState + "' could not be parsed to a BlockState!");
Map<String, String> pt = new HashMap<>(); Map<String, String> pt = new HashMap<>();
String g2 = m.group(2); String g2 = m.group(2);
@ -246,7 +182,7 @@ public class BlockState {
String blockId = m.group(1).trim(); String blockId = m.group(1).trim();
return new BlockState(version, blockId, pt); return new BlockState(blockId, pt);
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw new IllegalArgumentException("'" + serializedBlockState + "' could not be parsed to a BlockState!"); throw new IllegalArgumentException("'" + serializedBlockState + "' could not be parsed to a BlockState!");
} }

View File

@ -34,7 +34,7 @@ public interface Chunk {
LightData getLightData(int x, int y, int z, LightData target); LightData getLightData(int x, int y, int z, LightData target);
Biome getBiome(int x, int y, int z); int getBiome(int x, int y, int z);
int getMaxY(int x, int z); int getMaxY(int x, int z);

View File

@ -22,36 +22,43 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.sponge; package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.common.plugin.text.Text;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.message.MessageChannelEvent;
import org.spongepowered.api.event.network.ClientConnectionEvent;
public class EventForwarder { import java.util.Objects;
private final ServerEventListener listener; public class ResourcePackBlock<T extends ResourcePackBlock<T>> extends Block<T> {
public EventForwarder(ServerEventListener listener) { private final ResourcePack resourcePack;
this.listener = listener; private BlockProperties properties;
} private Biome biome;
public ResourcePackBlock(ResourcePack resourcePack, World world, int x, int y, int z) {
super(world, x, y, z);
this.resourcePack = Objects.requireNonNull(resourcePack);
}
@Override
protected void reset() {
super.reset();
this.properties = null;
this.biome = null;
}
public BlockProperties getProperties() {
if (properties == null) properties = resourcePack.getBlockProperties(getBlockState());
return properties;
}
public Biome getBiome() {
if (biome == null) biome = resourcePack.getBiome(getBiomeId());
return biome;
}
public ResourcePack getResourcePack() {
return resourcePack;
}
@Listener(order = Order.POST)
public void onPlayerJoin(ClientConnectionEvent.Join evt) {
listener.onPlayerJoin(evt.getTargetEntity().getUniqueId());
}
@Listener(order = Order.POST)
public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) {
listener.onPlayerJoin(evt.getTargetEntity().getUniqueId());
}
@Listener(order = Order.POST)
public void onPlayerChat(MessageChannelEvent.Chat evt) {
listener.onChatMessage(Text.of(evt.getMessage().toPlain()));
}
} }

View File

@ -26,7 +26,6 @@ package de.bluecolored.bluemap.core.world;
import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
@ -93,22 +92,6 @@ public class SlicedWorld implements World {
return world.getRegionGrid(); return world.getRegionGrid();
} }
@Override
public Biome getBiome(int x, int y, int z) {
return world.getBiome(x, y, z);
}
@Override
public BlockProperties getBlockProperties(BlockState blockState) {
return world.getBlockProperties(blockState);
}
@Override
public BlockState getBlockState(int x, int y, int z) {
if (!isInside(x, y, z)) return BlockState.AIR;
return world.getBlockState(x, y, z);
}
@Override @Override
public Chunk getChunk(int x, int z) { public Chunk getChunk(int x, int z) {
return world.getChunk(x, z); return world.getChunk(x, z);
@ -153,11 +136,6 @@ public class SlicedWorld implements World {
world.cleanUpChunkCache(); world.cleanUpChunkCache();
} }
@Override
public BlockPropertiesMapper getBlockPropertiesMapper() {
return world.getBlockPropertiesMapper();
}
private boolean isInside(int x, int z) { private boolean isInside(int x, int z) {
return return
x >= min.getX() && x >= min.getX() &&

View File

@ -26,7 +26,6 @@ package de.bluecolored.bluemap.core.world;
import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
@ -57,21 +56,6 @@ public interface World {
Grid getRegionGrid(); Grid getRegionGrid();
/**
* Returns the {@link Biome} on the specified position or the default biome if the block is not generated yet.
*/
Biome getBiome(int x, int y, int z);
/**
* Returns the {@link BlockState} on the specified position or an air-block if the block is not generated yet.
*/
BlockState getBlockState(int x, int y, int z);
/**
* Returns the BlockProperties for a block-state
*/
BlockProperties getBlockProperties(BlockState blockState);
/** /**
* Returns the {@link Chunk} on the specified block-position * Returns the {@link Chunk} on the specified block-position
*/ */
@ -83,7 +67,7 @@ public interface World {
Chunk getChunk(int x, int z); Chunk getChunk(int x, int z);
/** /**
* Returns the Chunk on the specified chunk-position * Returns the {@link Region} on the specified region-position
*/ */
Region getRegion(int x, int z); Region getRegion(int x, int z);
@ -107,10 +91,5 @@ public interface World {
* Cleans up invalid cache-entries to free up memory * Cleans up invalid cache-entries to free up memory
*/ */
void cleanUpChunkCache(); void cleanUpChunkCache();
/**
* Returns the block-properties manager used for this world
*/
BlockPropertiesMapper getBlockPropertiesMapper();
} }

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "bluemap:missing" }
}
}

View File

@ -1,6 +0,0 @@
{
"parent": "block/cube_all",
"textures": {
"all": "bluemap:blocks/missing"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "barrier" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/black_head" },
"part=head,facing=east": { "model": "bed/black_head", "y": 90 },
"part=head,facing=south": { "model": "bed/black_head", "y": 180 },
"part=head,facing=west": { "model": "bed/black_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/black_foot" },
"part=foot,facing=east": { "model": "bed/black_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/black_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/black_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/blue_head" },
"part=head,facing=east": { "model": "bed/blue_head", "y": 90 },
"part=head,facing=south": { "model": "bed/blue_head", "y": 180 },
"part=head,facing=west": { "model": "bed/blue_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/blue_foot" },
"part=foot,facing=east": { "model": "bed/blue_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/blue_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/blue_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/brown_head" },
"part=head,facing=east": { "model": "bed/brown_head", "y": 90 },
"part=head,facing=south": { "model": "bed/brown_head", "y": 180 },
"part=head,facing=west": { "model": "bed/brown_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/brown_foot" },
"part=foot,facing=east": { "model": "bed/brown_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/brown_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/brown_foot", "y": 270 }
}
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "bubble_column" }
}
}

View File

@ -1,13 +0,0 @@
{
"variants": {
"type=single,facing=north": { "model": "chest/normal", "y": 180 },
"type=single,facing=east": { "model": "chest/normal", "y": 270 },
"type=single,facing=south": { "model": "chest/normal" },
"type=single,facing=west": { "model": "chest/normal", "y":90 },
"type=right,facing=north": { "model": "chest/normal_double", "y": 180 },
"type=right,facing=east": { "model": "chest/normal_double", "y": 270 },
"type=right,facing=south": { "model": "chest/normal_double" },
"type=right,facing=west": { "model": "chest/normal_double", "y":90 },
"type=left": { "model": "chest/left" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/cyan_head" },
"part=head,facing=east": { "model": "bed/cyan_head", "y": 90 },
"part=head,facing=south": { "model": "bed/cyan_head", "y": 180 },
"part=head,facing=west": { "model": "bed/cyan_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/cyan_foot" },
"part=foot,facing=east": { "model": "bed/cyan_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/cyan_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/cyan_foot", "y": 270 }
}
}

View File

@ -1,8 +0,0 @@
{
"variants": {
"facing=north": { "model": "chest/ender", "y": 180 },
"facing=east": { "model": "chest/ender", "y": 270 },
"facing=south": { "model": "chest/ender" },
"facing=west": { "model": "chest/ender", "y":90 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/gray_head" },
"part=head,facing=east": { "model": "bed/gray_head", "y": 90 },
"part=head,facing=south": { "model": "bed/gray_head", "y": 180 },
"part=head,facing=west": { "model": "bed/gray_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/gray_foot" },
"part=foot,facing=east": { "model": "bed/gray_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/gray_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/gray_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/green_head" },
"part=head,facing=east": { "model": "bed/green_head", "y": 90 },
"part=head,facing=south": { "model": "bed/green_head", "y": 180 },
"part=head,facing=west": { "model": "bed/green_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/green_foot" },
"part=foot,facing=east": { "model": "bed/green_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/green_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/green_foot", "y": 270 }
}
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "lava" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/light_blue_head" },
"part=head,facing=east": { "model": "bed/light_blue_head", "y": 90 },
"part=head,facing=south": { "model": "bed/light_blue_head", "y": 180 },
"part=head,facing=west": { "model": "bed/light_blue_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/light_blue_foot" },
"part=foot,facing=east": { "model": "bed/light_blue_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/light_blue_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/light_blue_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/light_gray_head" },
"part=head,facing=east": { "model": "bed/light_gray_head", "y": 90 },
"part=head,facing=south": { "model": "bed/light_gray_head", "y": 180 },
"part=head,facing=west": { "model": "bed/light_gray_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/light_gray_foot" },
"part=foot,facing=east": { "model": "bed/light_gray_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/light_gray_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/light_gray_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/lime_head" },
"part=head,facing=east": { "model": "bed/lime_head", "y": 90 },
"part=head,facing=south": { "model": "bed/lime_head", "y": 180 },
"part=head,facing=west": { "model": "bed/lime_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/lime_foot" },
"part=foot,facing=east": { "model": "bed/lime_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/lime_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/lime_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/magenta_head" },
"part=head,facing=east": { "model": "bed/magenta_head", "y": 90 },
"part=head,facing=south": { "model": "bed/magenta_head", "y": 180 },
"part=head,facing=west": { "model": "bed/magenta_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/magenta_foot" },
"part=foot,facing=east": { "model": "bed/magenta_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/magenta_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/magenta_foot", "y": 270 }
}
}

View File

@ -1,20 +0,0 @@
{
"variants": {
"rotation=0": { "model": "sign/oak" },
"rotation=1": { "model": "sign/oak", "y": 22.5 },
"rotation=2": { "model": "sign/oak", "y": 45 },
"rotation=3": { "model": "sign/oak", "y": 67.5 },
"rotation=4": { "model": "sign/oak", "y": 90 },
"rotation=5": { "model": "sign/oak", "y": 112.5 },
"rotation=6": { "model": "sign/oak", "y": 135 },
"rotation=7": { "model": "sign/oak", "y": 157.5 },
"rotation=8": { "model": "sign/oak", "y": 180 },
"rotation=9": { "model": "sign/oak", "y": 202.5 },
"rotation=10": { "model": "sign/oak", "y": 225 },
"rotation=11": { "model": "sign/oak", "y": 247.5 },
"rotation=12": { "model": "sign/oak", "y": 270 },
"rotation=13": { "model": "sign/oak", "y": 292.5 },
"rotation=14": { "model": "sign/oak", "y": 315 },
"rotation=15": { "model": "sign/oak", "y": 337.5 }
}
}

View File

@ -1,8 +0,0 @@
{
"variants": {
"facing=south": { "model": "sign/wall_oak" },
"facing=west": { "model": "sign/wall_oak", "y": 90 },
"facing=north": { "model": "sign/wall_oak", "y": 180 },
"facing=east": { "model": "sign/wall_oak", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/orange_head" },
"part=head,facing=east": { "model": "bed/orange_head", "y": 90 },
"part=head,facing=south": { "model": "bed/orange_head", "y": 180 },
"part=head,facing=west": { "model": "bed/orange_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/orange_foot" },
"part=foot,facing=east": { "model": "bed/orange_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/orange_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/orange_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/pink_head" },
"part=head,facing=east": { "model": "bed/pink_head", "y": 90 },
"part=head,facing=south": { "model": "bed/pink_head", "y": 180 },
"part=head,facing=west": { "model": "bed/pink_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/pink_foot" },
"part=foot,facing=east": { "model": "bed/pink_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/pink_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/pink_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/purple_head" },
"part=head,facing=east": { "model": "bed/purple_head", "y": 90 },
"part=head,facing=south": { "model": "bed/purple_head", "y": 180 },
"part=head,facing=west": { "model": "bed/purple_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/purple_foot" },
"part=foot,facing=east": { "model": "bed/purple_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/purple_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/purple_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/red_head" },
"part=head,facing=east": { "model": "bed/red_head", "y": 90 },
"part=head,facing=south": { "model": "bed/red_head", "y": 180 },
"part=head,facing=west": { "model": "bed/red_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/red_foot" },
"part=foot,facing=east": { "model": "bed/red_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/red_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/red_foot", "y": 270 }
}
}

View File

@ -1,13 +0,0 @@
{
"variants": {
"type=single,facing=north": { "model": "chest/trapped", "y": 180 },
"type=single,facing=east": { "model": "chest/trapped", "y": 270 },
"type=single,facing=south": { "model": "chest/trapped" },
"type=single,facing=west": { "model": "chest/trapped", "y":90 },
"type=right,facing=north": { "model": "chest/trapped_double", "y": 180 },
"type=right,facing=east": { "model": "chest/trapped_double", "y": 270 },
"type=right,facing=south": { "model": "chest/trapped_double" },
"type=right,facing=west": { "model": "chest/trapped_double", "y":90 },
"type=left": { "model": "chest/left" }
}
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "water" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/white_head" },
"part=head,facing=east": { "model": "bed/white_head", "y": 90 },
"part=head,facing=south": { "model": "bed/white_head", "y": 180 },
"part=head,facing=west": { "model": "bed/white_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/white_foot" },
"part=foot,facing=east": { "model": "bed/white_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/white_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/white_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/yellow_head" },
"part=head,facing=east": { "model": "bed/yellow_head", "y": 90 },
"part=head,facing=south": { "model": "bed/yellow_head", "y": 180 },
"part=head,facing=west": { "model": "bed/yellow_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/yellow_foot" },
"part=foot,facing=east": { "model": "bed/yellow_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/yellow_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/yellow_foot", "y": 270 }
}
}

View File

@ -1,39 +0,0 @@
{
"elements": [
{
"from": [0, 0, 13],
"to": [3, 3, 16],
"faces": {
"north": {"uv": [14.75, 0.75, 15.5, 1.5], "texture": "#bed"},
"east": {"uv": [14, 0.75, 14.75, 1.5], "texture": "#bed"},
"south": {"uv": [13.25, 0.75, 14, 1.5], "texture": "#bed"},
"west": {"uv": [12.5, 0.75, 13.25, 1.5], "texture": "#bed"},
"up": {"uv": [13.25, 0, 14, 0.75], "texture": "#bed"},
"down": {"uv": [14, 0, 14.75, 0.75], "texture": "#bed"}
}
},
{
"from": [13, 0, 13],
"to": [16, 3, 16],
"faces": {
"north": {"uv": [14, 3.75, 14.75, 4.5], "texture": "#bed"},
"east": {"uv": [13.25, 3.75, 14, 4.5], "texture": "#bed"},
"south": {"uv": [12.5, 3.75, 13.25, 4.5], "texture": "#bed"},
"west": {"uv": [14.75, 3.75, 15.5, 4.5], "texture": "#bed"},
"up": {"uv": [13.25, 3, 14, 3.75], "texture": "#bed"},
"down": {"uv": [14, 3, 14.75, 3.75], "texture": "#bed"}
}
},
{
"from": [0, 3, 0],
"to": [16, 9, 16],
"faces": {
"east": {"uv": [5.5, 7, 7, 11], "rotation": 90, "texture": "#bed"},
"south": {"uv": [5.5, 7, 9.5, 5.5], "texture": "#bed"},
"west": {"uv": [0, 7, 1.5, 11], "rotation": 270, "texture": "#bed"},
"up": {"uv": [1.5, 7, 5.5, 11], "texture": "#bed"},
"down": {"uv": [7, 7, 11, 11], "rotation": 180, "texture": "#bed"}
}
}
]
}

View File

@ -1,39 +0,0 @@
{
"elements": [
{
"from": [0, 0, 0],
"to": [3, 3, 3],
"faces": {
"north": {"uv": [12.5, 2.25, 13.25, 3], "texture": "#bed"},
"east": {"uv": [14.75, 2.25, 15.5, 3], "texture": "#bed"},
"south": {"uv": [14, 2.25, 14.75, 3], "texture": "#bed"},
"west": {"uv": [13.25, 2.25, 14, 3], "texture": "#bed"},
"up": {"uv": [13.25, 1.5, 14, 2.25], "texture": "#bed"},
"down": {"uv": [14, 1.5, 14.75, 2.25], "texture": "#bed"}
}
},
{
"from": [13, 0, 0],
"to": [16, 3, 3],
"faces": {
"north": {"uv": [13.25, 5.25, 14, 6], "texture": "#bed"},
"east": {"uv": [12.5, 5.25, 13.25, 6], "texture": "#bed"},
"south": {"uv": [14.75, 5.25, 15.5, 6], "texture": "#bed"},
"west": {"uv": [14, 5.25, 14.75, 6], "texture": "#bed"},
"up": {"uv": [13.25, 4.5, 14, 5.25], "texture": "#bed"},
"down": {"uv": [14, 4.5, 14.75, 5.25], "texture": "#bed"}
}
},
{
"from": [0, 3, 0],
"to": [16, 9, 16],
"faces": {
"north": {"uv": [1.5, 1.5, 5.5, 0], "texture": "#bed"},
"east": {"uv": [5.5, 1.5, 7, 5.5], "rotation": 90, "texture": "#bed"},
"west": {"uv": [0, 1.5, 1.5, 5.5], "rotation": 270, "texture": "#bed"},
"up": {"uv": [1.5, 1.5, 5.5, 5.5], "texture": "#bed"},
"down": {"uv": [7, 1.5, 11, 5.5], "rotation": 180, "texture": "#bed"}
}
}
]
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_foot",
"textures": {
"bed": "entity/bed/black"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_head",
"textures": {
"bed": "entity/bed/black"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_foot",
"textures": {
"bed": "entity/bed/blue"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_head",
"textures": {
"bed": "entity/bed/blue"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_foot",
"textures": {
"bed": "entity/bed/brown"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_head",
"textures": {
"bed": "entity/bed/brown"
}
}

Some files were not shown because too many files have changed in this diff Show More