Merge branch 'mc/1.13' into mc/1.12

This commit is contained in:
Blue (Lukas Rieger) 2020-02-09 17:24:38 +01:00
commit 3d43ac5599
9 changed files with 344 additions and 94 deletions

View File

@ -75,7 +75,8 @@ public static Chunk create(MCAWorld world, CompoundTag chunkTag) throws IOExcept
int version = chunkTag.getInt("DataVersion");
if (version <= 1343) return new ChunkAnvil112(world, chunkTag);
return new ChunkAnvil113(world, chunkTag);
if (version <= 1976) return new ChunkAnvil113(world, chunkTag);
return new ChunkAnvil115(world, chunkTag);
}
public static Chunk empty(MCAWorld world, Vector2i chunkPos) {

View File

@ -116,9 +116,9 @@ public LightData getLightData(Vector3i pos) {
public Biome getBiome(Vector3i pos) {
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
int z = pos.getZ() & 0xF;
int biomeByteIndex = z * 16 + x;
int biomeIntIndex = z * 16 + x;
return biomeIdMapper.get(biomes[biomeByteIndex]);
return biomeIdMapper.get(biomes[biomeIntIndex]);
}
private class Section {

View File

@ -0,0 +1,234 @@
/*
* 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 java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.flowpowered.math.vector.Vector3i;
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.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
import net.querz.nbt.ByteArrayTag;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.IntArrayTag;
import net.querz.nbt.ListTag;
import net.querz.nbt.StringTag;
import net.querz.nbt.Tag;
import net.querz.nbt.mca.MCAUtil;
public class ChunkAnvil115 extends Chunk {
private BiomeMapper biomeIdMapper;
private boolean isGenerated;
private Section[] sections;
private int[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil115(MCAWorld world, CompoundTag chunkTag) {
super(world, chunkTag);
biomeIdMapper = getWorld().getBiomeIdMapper();
CompoundTag levelData = chunkTag.getCompoundTag("Level");
String status = levelData.getString("Status");
isGenerated = status.equals("full") || status.equals("spawn"); // full is normal fully generated and spawn seems to be converted from old format but not yet loaded if you optimized your world
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) sections[section.getSectionY()] = section;
}
}
Tag<?> tag = levelData.get("Biomes"); //tag can be byte-array or int-array
if (tag instanceof ByteArrayTag) {
byte[] bs = ((ByteArrayTag) tag).getValue();
biomes = new int[bs.length];
for (int i = 0; i < bs.length; i++) {
biomes[i] = bs[i] & 0xFF;
}
}
else if (tag instanceof IntArrayTag) {
biomes = ((IntArrayTag) tag).getValue();
}
if (biomes == null || biomes.length == 0) {
biomes = new int[2048];
}
}
@Override
public boolean isGenerated() {
return isGenerated;
}
@Override
public BlockState getBlockState(Vector3i pos) {
int sectionY = MCAUtil.blockToChunk(pos.getY());
Section section = this.sections[sectionY];
if (section == null) return BlockState.AIR;
return section.getBlockState(pos);
}
@Override
public LightData getLightData(Vector3i pos) {
int sectionY = MCAUtil.blockToChunk(pos.getY());
Section section = this.sections[sectionY];
if (section == null) return LightData.FULL;
return section.getLightData(pos);
}
@Override
public Biome getBiome(Vector3i pos) {
int x = (pos.getX() & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
int z = (pos.getZ() & 0xF) / 4;
int y = pos.getY() / 4;
int biomeIntIndex = y * 16 + z * 4 + x;
return biomeIdMapper.get(biomes[biomeIntIndex]);
}
private class Section {
private static final String AIR_ID = "minecraft:air";
private int sectionY;
private byte[] blockLight;
private byte[] skyLight;
private long[] blocks;
private BlockState[] palette;
@SuppressWarnings("unchecked")
public Section(CompoundTag sectionData) {
this.sectionY = sectionData.getByte("Y");
this.blockLight = sectionData.getByteArray("BlockLight");
if (blockLight.length == 0) blockLight = new byte[2048];
this.skyLight = sectionData.getByteArray("SkyLight");
if (skyLight.length == 0) skyLight = new byte[2048];
this.blocks = sectionData.getLongArray("BlockStates");
//read block palette
ListTag<CompoundTag> paletteTag = (ListTag<CompoundTag>) sectionData.getListTag("Palette");
if (paletteTag != null) {
this.palette = new BlockState[paletteTag.size()];
for (int i = 0; i < this.palette.length; i++) {
CompoundTag stateTag = paletteTag.get(i);
String id = stateTag.getString("Name"); //shortcut to save time and memory
if (id.equals(AIR_ID)) {
palette[i] = BlockState.AIR;
continue;
}
Map<String, String> properties = new HashMap<>();
if (stateTag.containsKey("Properties")) {
CompoundTag propertiesTag = stateTag.getCompoundTag("Properties");
for (Entry<String, Tag<?>> property : propertiesTag) {
properties.put(property.getKey().toLowerCase(), ((StringTag) property.getValue()).getValue().toLowerCase());
}
}
palette[i] = new BlockState(id, properties);
}
} else {
this.palette = new BlockState[0];
}
}
public int getSectionY() {
return sectionY;
}
public BlockState getBlockState(Vector3i pos) {
if (blocks.length == 0) return BlockState.AIR;
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
int y = pos.getY() & 0xF;
int z = pos.getZ() & 0xF;
int blockIndex = y * 256 + z * 16 + x;
int bitsPerBlock = blocks.length * 64 / 4096; //64 bits per long and 4096 blocks per section
int index = blockIndex * bitsPerBlock;
int firstLong = index >> 6; // index / 64
int bitoffset = index & 0x3F; // Math.floorMod(index, 64)
long value = blocks[firstLong] >>> bitoffset;
if (bitoffset > 0 && firstLong + 1 < blocks.length) {
long value2 = blocks[firstLong + 1];
value2 = value2 << -bitoffset;
value = value | value2;
}
value = value & (0xFFFFFFFFFFFFFFFFL >>> -bitsPerBlock);
if (value >= palette.length) {
Logger.global.noFloodWarning("palettewarning", "Got palette value " + value + " but palette has size of " + palette.length + " (Future occasions of this error will not be logged)");
return BlockState.MISSING;
}
return palette[(int) value];
}
public LightData getLightData(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 blockLight = getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf);
int skyLight = getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf);
return new LightData(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

@ -149,6 +149,19 @@ public BlockState getBlockState(Vector3i pos) {
}
}
@Override
public Biome getBiome(Vector3i pos) {
try {
Vector2i chunkPos = blockToChunk(pos);
Chunk chunk = getChunk(chunkPos);
return chunk.getBiome(pos);
} catch (IOException ex) {
throw new RuntimeException("Unexpected IO-Exception trying to read world-data!", ex);
}
}
@Override
public Block getBlock(Vector3i pos) {
if (pos.getY() < getMinY()) {

View File

@ -28,6 +28,7 @@
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
@ -36,11 +37,13 @@
import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.util.ConfigUtils;
import de.bluecolored.bluemap.core.util.MathUtils;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.World;
import ninja.leaping.configurate.ConfigurationNode;
public class BlockColorCalculator {
@ -97,31 +100,28 @@ public Vector3f getBlockColor(Block block){
public Vector3f getWaterAverageColor(Block block){
Vector3f color = Vector3f.ZERO;
for (int x = -1; x <= 1; x++){
for (int z = -1; z <= 1; z++){
color = color.add(block.getRelativeBlock(x, 0, z).getBiome().getWaterColor());
}
int count = 0;
for (Biome biome : iterateAverageBiomes(block)) {
color = color.add(biome.getWaterColor());
count++;
}
return color.div(9f);
return color.div(count);
}
public Vector3f getFoliageAverageColor(Block block){
Vector3f color = Vector3f.ZERO;
for (int x = -1; x <= 1; x++){
for (int z = -1; z <= 1; z++){
color = color.add(getFoliageColor(block.getRelativeBlock(x, 0, z)));
}
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
int count = 0;
for (Biome biome : iterateAverageBiomes(block)) {
color = color.add(getFoliageColor(biome, blocksAboveSeaLevel));
count++;
}
return color.div(9f);
}
public Vector3f getFoliageColor(Block block){
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
return getFoliageColor(block.getBiome(), blocksAboveSeaLevel);
return color.div(count);
}
public Vector3f getFoliageColor(Biome biome, int blocksAboveSeaLevel){
@ -134,18 +134,15 @@ public Vector3f getFoliageColor(Biome biome, int blocksAboveSeaLevel){
public Vector3f getGrassAverageColor(Block block){
Vector3f color = Vector3f.ZERO;
for (int x = -1; x <= 1; x++){
for (int z = -1; z <= 1; z++){
color = color.add(getGrassColor(block.getRelativeBlock(x, 0, z)));
}
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
int count = 0;
for (Biome biome : iterateAverageBiomes(block)) {
color = color.add(getGrassColor(biome, blocksAboveSeaLevel));
count++;
}
return color.div(9f);
}
public Vector3f getGrassColor(Block block){
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
return getGrassColor(block.getBiome(), blocksAboveSeaLevel);
return color.div(count);
}
public Vector3f getGrassColor(Biome biome, int blocksAboveSeaLevel){
@ -168,6 +165,45 @@ private Vector2f getColorMapPosition(Biome biome, int blocksAboveSeaLevel){
float adjHumidity = (float) GenericMath.clamp(biome.getHumidity(), 0d, 1d) * adjTemp;
return new Vector2f(1 - adjTemp, 1 - adjHumidity);
}
private Iterable<Biome> iterateAverageBiomes(Block block){
Vector3i pos = block.getPosition();
Vector3i radius = new Vector3i(2, 1, 2);
final World world = block.getWorld();
final int sx = pos.getX() - radius.getX(),
sy = pos.getY() - radius.getY(),
sz = pos.getZ() - radius.getZ();
final int mx = pos.getX() + radius.getX(),
my = pos.getY() + radius.getY(),
mz = pos.getZ() + radius.getZ();
return () -> new Iterator<Biome>() {
private int x = sx,
y = sy,
z = sz;
@Override
public boolean hasNext() {
return z < mz || y < my || x < mx;
}
@Override
public Biome next() {
x++;
if (x > mx) {
x = sx;
y++;
}
if (y > my) {
y = sy;
z++;
}
return world.getBiome(new Vector3i(x, y, z));
}
};
}
public BufferedImage getFoliageMap() {
return foliageMap;

View File

@ -79,6 +79,11 @@ public int getMinY() {
return world.getMinY();
}
@Override
public Biome getBiome(Vector3i pos) {
return world.getBiome(pos);
}
@Override
public Block getBlock(Vector3i pos) {
if (!isInside(pos)) return createAirBlock(pos);

View File

@ -56,6 +56,11 @@ default int getMaxY() {
default int getMinY() {
return 0;
}
/**
* Returns the Biome on the specified position or the default biome if the block is not generated yet.
*/
Biome getBiome(Vector3i pos);
/**
* Returns the Block on the specified position or an air-block if the block is not generated yet.

View File

@ -1,6 +1,7 @@
{
"default": "@foliage",
"minecraft:water": "@water",
"minecraft:cauldron": "@water",
"minecraft:grass": "@grass",
"minecraft:tall_grass": "@grass",
"minecraft:double_grass": "@grass",

View File

@ -2,21 +2,28 @@
BlueMap is a tool that generates 3d-maps of your Minecraft worlds and displays them in your browser. Take a look at [this demo](https://bluecolored.de/bluemap). It is really easy to set up - almost plug-and-play - if you use the integrated web-server (optional).
The Sponge-Plugin automatically updates your map as soon as something changes in your world, as well as rendering newly generated terrain and managing the render-tasks.
The Sponge/Spigot-Plugin automatically updates your map as soon as something changes in your world, as well as rendering newly generated terrain and managing the render-tasks.
**BlueMap is currently in a really early development state!**
**BlueMap is currently in an early development state!**
The majority of features are still missing, and some blocks - especially tile-entities - will not render correctly/at all.
A lot of features are still missing, and some blocks - especially some tile-entities - will not render correctly/at all.
See below for a list of what is planned for future releases.
### Clone
Easy:
### Download
You can choose a version and download BlueMap from [here](https://github.com/BlueMap-Minecraft/BlueMap/releases).
`git clone https://github.com/BlueMap-Minecraft/BlueMap.git`
### Using BlueMap
BlueMap can be used on the command-line, or as a plugin for your Sponge/Spigot/Paper-Server. Read the [wiki](https://github.com/BlueMap-Minecraft/BlueMap/wiki) to get started!
### Discord
If you have a question, help others using BlueMap or get the latest news and info you are welcome to join us [on Discord](https://discord.gg/zmkyJa3)!
### Clone
If you have git installed, simply use the command `git clone https://github.com/BlueMap-Minecraft/BlueMap.git` to clone BlueMap.
### Build
In order to build BlueMap you simply need to run the `./gradlew build` command.
You can find the compiled JAR file in `./build/libs`
In order to build BlueMap you simply need to run the `./gradlew build` command in BlueMap's root directory.
You can find the compiled JAR file in `./build/libs`.
### Issues / Suggestions
You found a bug, have another issue or a suggestion? Please create an issue [here](https://github.com/BlueMap-Minecraft/BlueMap/issues)!
@ -25,70 +32,18 @@ You found a bug, have another issue or a suggestion? Please create an issue [her
You are welcome to contribute!
Just create a pull request with your changes :)
## Using the CLI
BlueMap can be used on the command-line, to render your Minecraft-Worlds *(Currently supported versions: 1.12 - 1.14)*.
Use `java -jar bluemap.jar` and BlueMap will generate a default config in the current working directory. You then can configure your maps and even the webserver as you wish. Then, re-run the command and BlueMap will render all the configured maps for you and start the webserver if you turned it on in the config.
To only run the webserver, just don't define any maps in the config.
You can use `-c <config-file>` on the command-line to define a different configuration-file.
## Using the Sponge-Plugin
### Getting started
BlueMap is mostly plug-and-play. Just install it like every other Sponge-Plugin and start your server. BlueMap will then generate a config-file for you in the `./config/bluemap/` folder. Here you can configure your maps and the webserver. The config has many useful comments in it, explaining everything :)
**Before BlueMap can render anything,** it needs one more thing: resources! To render all the block-models, BlueMap makes use of the default minecraft-resources. Since they are property of mojang i can not include them in the plugin. Fortunately BlueMap can download them from mojangs servers for you, but you need to explicitly agree to this in the config! Simply change the `accept-download: false` setting to `accept-download: true`, and run the `/bluemap reload` command.
After downloading the resources, BlueMap will start updating the configured worlds. To render the whole world for a start, you can use this command `/bluemap render [world]`.
Then, head over to `http://<your-server-ip>:8100/` and you should see your map! *(If there is only black, you might have to wait a little until BlueMap has rendered enough of the map. You can also try to zoom in: the hires-models are saved first)*
If you need help with the setup i will be happy to help you!
### Metrics and Webserver
**Bluemap uses [bStats](https://bstats.org/) and an own metrics-system and is hosting a web-server!**
Metrics are really useful to keep track of how the plugin is used and helps me stay motivated! Please turn them on :)
**bStats:** All data collected by bStats can be viewed here: https://bstats.org/plugin/sponge/BlueMap. bStats data-collection is controlled by the metrics-setting set in sponges configuration! *(Turned off by default)*
**own metrics:** Additionally to bStats, BlueMap is sending a super small report, containing only the implementation-name and the version of the BlueMap-plugin to my server (`https://metrics.bluecolored.de/bluemap`). I do this, because i might release some other implementations for BlueMap (like a CLI, or a forge-mod) that are not supported by bStats. Here is an example report:
```json
{
"implementation": "Sponge",
"version": "0.0.0"
}
```
This data-collection is also controlled by the metrics-setting set in sponges configuration! *(Turned off by default)*
**web-server:** The web-server is a core-functionality of this plugin. It is enabled by default but can be disabled in the plugin-config. By default the web-server is bound to the standard ip-address on port `8100` and is hosting the content of the `./bluemap/web/`-folder.
### Commands and Permissions
command | permission | description
--- | --- | ---
/bluemap | bluemap.status | displays BlueMaps render status
/bluemap reload | bluemap.reload | reloads all resources, configuration-files and the web-server
/bluemap pause | bluemap.pause | pauses all rendering
/bluemap resume | bluemap.resume | resumes all paused rendering
/bluemap render \[world\] | bluemap.rendertask.create.world | renders the whole world
*\[clickable command in /bluemap\]* | bluemap.rendertask.prioritize | prioritizes the clicked render-task
*\[clickable command in /bluemap\]* | bluemap.rendertask.remove | removes the clicked render-task
## Todo / planned features
### Todo / planned features
Here is a *(surely incomplete)* list of things that i want to include in future versions. *(They are not in any specific order. There is no guarantee that any of those things will ever be included.)*
- render tile-entities (chests, etc..)
- render entities
- render more tile-entities (banners, shulker-chests, etc..)
- render entities (armor-stands, item-frames, maybe even cows and such..)
- configurable markers / regions
- marker / region API
- free-flight-controls
- live player positions
- shaders for dynamic day/night
- more configurations
- better resource-pack support
- mod-support (or an easy way for modders to do so themselves)
- BlueMap as spigot plugin
- easier mod-integration
- BlueMap as forge mod
- more render-tasks (commands to render parts of your world)
- config to restrict map-generation to some bounds
- ability to display the world-border
- animated textures (if feasible)