Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-25 15:50:14 +01:00
Merge branch 'biomes'
Dieser Commit ist enthalten in:
Commit
7a99aa0ddf
@ -59,9 +59,7 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
@ -144,6 +142,7 @@ public class GeyserSession implements CommandSender {
|
|||||||
private final PreferencesCache preferencesCache;
|
private final PreferencesCache preferencesCache;
|
||||||
private final TagCache tagCache;
|
private final TagCache tagCache;
|
||||||
private WorldCache worldCache;
|
private WorldCache worldCache;
|
||||||
|
|
||||||
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
private final PlayerInventory playerInventory;
|
private final PlayerInventory playerInventory;
|
||||||
@ -189,6 +188,13 @@ public class GeyserSession implements CommandSender {
|
|||||||
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
|
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
|
||||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the differences between Java and Bedrock biome network IDs.
|
||||||
|
* If Java's ocean biome is 0, and Bedrock's is 0, it will not be in the list.
|
||||||
|
* If Java's bamboo biome is 42, and Bedrock's is 48, it will be in this list.
|
||||||
|
*/
|
||||||
|
private final Int2IntMap biomeTranslations = new Int2IntOpenHashMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of Vector3i positions to Java entities.
|
* A map of Vector3i positions to Java entities.
|
||||||
* Used for translating Bedrock block actions to Java entity actions.
|
* Used for translating Bedrock block actions to Java entity actions.
|
||||||
@ -492,7 +498,7 @@ public class GeyserSession implements CommandSender {
|
|||||||
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
|
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
|
||||||
|
|
||||||
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
|
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
|
||||||
biomeDefinitionListPacket.setDefinitions(Registries.BIOMES.get());
|
biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get());
|
||||||
upstream.sendPacket(biomeDefinitionListPacket);
|
upstream.sendPacket(biomeDefinitionListPacket);
|
||||||
|
|
||||||
AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();
|
AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();
|
||||||
|
@ -39,6 +39,7 @@ import org.geysermc.connector.entity.player.PlayerEntity;
|
|||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.network.translators.world.BiomeTranslator;
|
||||||
import org.geysermc.connector.utils.ChunkUtils;
|
import org.geysermc.connector.utils.ChunkUtils;
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
import org.geysermc.connector.utils.PluginMessageUtils;
|
import org.geysermc.connector.utils.PluginMessageUtils;
|
||||||
@ -66,6 +67,7 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
|
|||||||
}
|
}
|
||||||
session.setWorldName(packet.getWorldName());
|
session.setWorldName(packet.getWorldName());
|
||||||
|
|
||||||
|
BiomeTranslator.loadServerBiomes(session, packet.getDimensionCodec());
|
||||||
session.getTagCache().clear();
|
session.getTagCache().clear();
|
||||||
|
|
||||||
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
|
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
|
||||||
|
@ -40,7 +40,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
|
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
|
||||||
import org.geysermc.connector.utils.BiomeUtils;
|
import org.geysermc.connector.network.translators.world.BiomeTranslator;
|
||||||
import org.geysermc.connector.utils.ChunkUtils;
|
import org.geysermc.connector.utils.ChunkUtils;
|
||||||
|
|
||||||
@Translator(packet = ServerChunkDataPacket.class)
|
@Translator(packet = ServerChunkDataPacket.class)
|
||||||
@ -101,7 +101,7 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
|
|||||||
|
|
||||||
if (NEW_BIOME_WRITE) {
|
if (NEW_BIOME_WRITE) {
|
||||||
for (int i = 0; i < sectionCount; i++) {
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
BiomeUtils.toNewBedrockBiome(column.getBiomeData(), i).writeToNetwork(byteBuf);
|
BiomeTranslator.toNewBedrockBiome(session, column.getBiomeData(), i).writeToNetwork(byteBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// As of 1.17.10, Bedrock hardcodes to always read 32 biome sections
|
// As of 1.17.10, Bedrock hardcodes to always read 32 biome sections
|
||||||
@ -110,7 +110,7 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
|
|||||||
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
|
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
byteBuf.writeBytes(BiomeUtils.toBedrockBiome(column.getBiomeData())); // Biomes - 256 bytes
|
byteBuf.writeBytes(BiomeTranslator.toBedrockBiome(session, column.getBiomeData())); // Biomes - 256 bytes
|
||||||
}
|
}
|
||||||
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
||||||
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
||||||
|
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.network.translators.world;
|
||||||
|
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.world.chunk.BlockStorage;
|
||||||
|
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
|
||||||
|
import org.geysermc.connector.registry.Registries;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// Based off of ProtocolSupport's LegacyBiomeData.java:
|
||||||
|
// https://github.com/ProtocolSupport/ProtocolSupport/blob/b2cad35977f3fcb65bee57b9e14fc9c975f71d32/src/protocolsupport/protocol/typeremapper/legacy/LegacyBiomeData.java
|
||||||
|
// Array index formula by https://wiki.vg/Chunk_Format
|
||||||
|
public class BiomeTranslator {
|
||||||
|
|
||||||
|
public static void loadServerBiomes(GeyserSession session, CompoundTag codec) {
|
||||||
|
Int2IntMap biomeTranslations = session.getBiomeTranslations();
|
||||||
|
biomeTranslations.clear();
|
||||||
|
|
||||||
|
CompoundTag worldGen = codec.get("minecraft:worldgen/biome");
|
||||||
|
ListTag serverBiomes = worldGen.get("value");
|
||||||
|
|
||||||
|
for (Tag tag : serverBiomes) {
|
||||||
|
CompoundTag biomeTag = (CompoundTag) tag;
|
||||||
|
|
||||||
|
String javaIdentifier = ((StringTag) biomeTag.get("name")).getValue();
|
||||||
|
int bedrockId = Registries.BIOME_IDENTIFIERS.get().getOrDefault(javaIdentifier, -1);
|
||||||
|
int javaId = ((IntTag) biomeTag.get("id")).getValue();
|
||||||
|
|
||||||
|
if (bedrockId == -1) {
|
||||||
|
// There is no matching Bedrock variation for this biome; let's set the closest match based on biome category
|
||||||
|
String category = ((StringTag) ((CompoundTag) biomeTag.get("element")).get("category")).getValue();
|
||||||
|
String replacementBiome;
|
||||||
|
switch (category) {
|
||||||
|
case "extreme_hills":
|
||||||
|
replacementBiome = "minecraft:mountains";
|
||||||
|
break;
|
||||||
|
case "icy":
|
||||||
|
replacementBiome = "minecraft:ice_spikes";
|
||||||
|
break;
|
||||||
|
case "mesa":
|
||||||
|
replacementBiome = "minecraft:badlands";
|
||||||
|
break;
|
||||||
|
case "mushroom":
|
||||||
|
replacementBiome = "minecraft:mushroom_fields";
|
||||||
|
break;
|
||||||
|
case "nether":
|
||||||
|
replacementBiome = "minecraft:nether_wastes";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
replacementBiome = "minecraft:ocean"; // Typically ID 0 so a good default
|
||||||
|
break;
|
||||||
|
case "taiga":
|
||||||
|
case "jungle":
|
||||||
|
case "plains":
|
||||||
|
case "savanna":
|
||||||
|
case "the_end":
|
||||||
|
case "beach":
|
||||||
|
case "ocean":
|
||||||
|
case "desert":
|
||||||
|
case "river":
|
||||||
|
case "swamp":
|
||||||
|
replacementBiome = "minecraft:" + category;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bedrockId = Registries.BIOME_IDENTIFIERS.get().getInt(replacementBiome);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (javaId != bedrockId) {
|
||||||
|
// When we see the Java ID, we should instead apply the Bedrock ID
|
||||||
|
biomeTranslations.put(javaId, bedrockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBedrockBiome(GeyserSession session, int[] biomeData) {
|
||||||
|
byte[] bedrockData = new byte[256];
|
||||||
|
if (biomeData == null) {
|
||||||
|
return bedrockData;
|
||||||
|
}
|
||||||
|
Int2IntMap biomeTranslations = session.getBiomeTranslations();
|
||||||
|
|
||||||
|
for (int y = 0; y < 16; y += 4) {
|
||||||
|
for (int z = 0; z < 16; z += 4) {
|
||||||
|
for (int x = 0; x < 16; x += 4) {
|
||||||
|
int javaId = biomeData[((y >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3)];
|
||||||
|
byte biomeId = (byte) biomeTranslations.getOrDefault(javaId, javaId);
|
||||||
|
int offset = ((z + (y / 4)) << 4) | x;
|
||||||
|
Arrays.fill(bedrockData, offset, offset + 4, biomeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bedrockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockStorage toNewBedrockBiome(GeyserSession session, int[] biomeData, int ySection) {
|
||||||
|
Int2IntMap biomeTranslations = session.getBiomeTranslations();
|
||||||
|
// As of 1.17.10: the client expects the same format as a chunk but filled with biomes
|
||||||
|
BlockStorage storage = new BlockStorage(0);
|
||||||
|
|
||||||
|
int biomeY = ySection << 2;
|
||||||
|
int javaOffsetY = biomeY << 4;
|
||||||
|
// Each section of biome corresponding to a chunk section contains 4 * 4 * 4 entries
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
int javaId = biomeData[javaOffsetY | i];
|
||||||
|
int x = i & 3;
|
||||||
|
int y = (i >> 4) & 3;
|
||||||
|
int z = (i >> 2) & 3;
|
||||||
|
// Get the Bedrock biome ID override, or this ID if it's the same
|
||||||
|
int biomeId = biomeTranslations.getOrDefault(javaId, javaId);
|
||||||
|
int idx = storage.idFor(biomeId);
|
||||||
|
// Convert biome coordinates into block coordinates
|
||||||
|
// Bedrock expects a full 4096 blocks
|
||||||
|
for (int blockX = x << 2; blockX < (x << 2) + 4; blockX++) {
|
||||||
|
for (int blockZ = z << 2; blockZ < (z << 2) + 4; blockZ++) {
|
||||||
|
for (int blockY = y << 2; blockY < (y << 2) + 4; blockY++) {
|
||||||
|
storage.getBitArray().set(ChunkSection.blockPosition(blockX, blockY, blockZ), idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
}
|
@ -101,7 +101,7 @@ public class BlockStorage {
|
|||||||
this.bitArray = newBitArray;
|
this.bitArray = newBitArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int idFor(int runtimeId) {
|
public int idFor(int runtimeId) { // Set to public so we can reuse the palette ID for biomes
|
||||||
int index = this.palette.indexOf(runtimeId);
|
int index = this.palette.indexOf(runtimeId);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
return index;
|
return index;
|
||||||
|
@ -36,6 +36,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.PotionMixData;
|
|||||||
import it.unimi.dsi.fastutil.Pair;
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
|
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
|
||||||
import org.geysermc.connector.network.translators.effect.Effect;
|
import org.geysermc.connector.network.translators.effect.Effect;
|
||||||
import org.geysermc.connector.network.translators.sound.SoundHandler;
|
import org.geysermc.connector.network.translators.sound.SoundHandler;
|
||||||
@ -59,7 +60,12 @@ public class Registries {
|
|||||||
/**
|
/**
|
||||||
* A registry holding a CompoundTag of all the known biomes.
|
* A registry holding a CompoundTag of all the known biomes.
|
||||||
*/
|
*/
|
||||||
public static final SimpleRegistry<NbtMap> BIOMES = SimpleRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT);
|
public static final SimpleRegistry<NbtMap> BIOMES_NBT = SimpleRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapped registry which stores Java biome identifiers and their Bedrock biome identifier.
|
||||||
|
*/
|
||||||
|
public static final SimpleRegistry<Object2IntMap<String>> BIOME_IDENTIFIERS = SimpleRegistry.create("mappings/biomes.json", BiomeIdentifierRegistryLoader::new);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapped registry which stores a block entity identifier to its {@link BlockEntityTranslator}.
|
* A mapped registry which stores a block entity identifier to its {@link BlockEntityTranslator}.
|
||||||
|
@ -58,7 +58,7 @@ import java.util.function.Consumer;
|
|||||||
* however it demonstrates a fairly basic use case of how this system works. Typically
|
* however it demonstrates a fairly basic use case of how this system works. Typically
|
||||||
* though, the first parameter would be a location of some sort, such as a file path
|
* though, the first parameter would be a location of some sort, such as a file path
|
||||||
* where the loader will load the mappings from. The NBT registry is a good reference
|
* where the loader will load the mappings from. The NBT registry is a good reference
|
||||||
* point for something both simple and practical. See {@link Registries#BIOMES} and
|
* point for something both simple and practical. See {@link Registries#BIOMES_NBT} and
|
||||||
* {@link org.geysermc.connector.registry.loader.NbtRegistryLoader}.
|
* {@link org.geysermc.connector.registry.loader.NbtRegistryLoader}.
|
||||||
*
|
*
|
||||||
* @param <M> the value being held by the registry
|
* @param <M> the value being held by the registry
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.registry.loader;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.utils.FileUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class BiomeIdentifierRegistryLoader implements RegistryLoader<String, Object2IntMap<String>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object2IntMap<String> load(String input) {
|
||||||
|
// As of Bedrock Edition 1.17.10 with the experimental toggle, any unmapped biome identifier sent to the client
|
||||||
|
// crashes the client. Therefore, we need to have a list of all valid Bedrock biome IDs with which we can use from.
|
||||||
|
// The server sends the corresponding Java network IDs, so we don't need to worry about that now.
|
||||||
|
|
||||||
|
// Reference variable for Jackson to read off of
|
||||||
|
TypeReference<Map<String, BiomeEntry>> biomeEntriesType = new TypeReference<Map<String, BiomeEntry>>() { };
|
||||||
|
Map<String, BiomeEntry> biomeEntries;
|
||||||
|
|
||||||
|
try (InputStream stream = FileUtils.getResource("mappings/biomes.json")) {
|
||||||
|
biomeEntries = GeyserConnector.JSON_MAPPER.readValue(stream, biomeEntriesType);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError("Unable to load Bedrock runtime biomes", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object2IntMap<String> biomes = new Object2IntOpenHashMap<>();
|
||||||
|
for (Map.Entry<String, BiomeEntry> biome : biomeEntries.entrySet()) {
|
||||||
|
// Java Edition identifier -> Bedrock integer ID
|
||||||
|
biomes.put(biome.getKey(), biome.getValue().bedrockId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return biomes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BiomeEntry {
|
||||||
|
/**
|
||||||
|
* The Bedrock network ID for this biome.
|
||||||
|
*/
|
||||||
|
@JsonProperty("bedrock_id")
|
||||||
|
private int bedrockId;
|
||||||
|
}
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.geysermc.connector.utils;
|
|
||||||
|
|
||||||
import org.geysermc.connector.network.translators.world.chunk.BlockStorage;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
// Based off of ProtocolSupport's LegacyBiomeData.java:
|
|
||||||
// https://github.com/ProtocolSupport/ProtocolSupport/blob/b2cad35977f3fcb65bee57b9e14fc9c975f71d32/src/protocolsupport/protocol/typeremapper/legacy/LegacyBiomeData.java
|
|
||||||
// Array index formula by https://wiki.vg/Chunk_Format
|
|
||||||
public class BiomeUtils {
|
|
||||||
public static byte[] toBedrockBiome(int[] biomeData) {
|
|
||||||
byte[] bedrockData = new byte[256];
|
|
||||||
if (biomeData == null) {
|
|
||||||
return bedrockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = 0; y < 16; y += 4) {
|
|
||||||
for (int z = 0; z < 16; z += 4) {
|
|
||||||
for (int x = 0; x < 16; x += 4) {
|
|
||||||
byte biomeId = (byte) biomeID(biomeData, x, y, z);
|
|
||||||
int offset = ((z + (y / 4)) << 4) | x;
|
|
||||||
Arrays.fill(bedrockData, offset, offset + 4, biomeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bedrockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BlockStorage toNewBedrockBiome(int[] biomeData, int ySection) {
|
|
||||||
BlockStorage storage = new BlockStorage(0);
|
|
||||||
int blockY = ySection << 4;
|
|
||||||
int i = 0;
|
|
||||||
// Iterate over biomes like a chunk, grab the biome from Java, and add it to Bedrock's biome palette
|
|
||||||
// Might be able to be optimized by iterating over Java's biome data section?? Unsure.
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
for (int z = 0; z < 16; z++) {
|
|
||||||
for (int y = blockY; y < (blockY + 16); y++) {
|
|
||||||
int biomeId = biomeID(biomeData, x, y, z);
|
|
||||||
storage.setFullBlock(i, biomeId);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int biomeID(int[] biomeData, int x, int y, int z) {
|
|
||||||
int biomeId = biomeData[((y >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3)];
|
|
||||||
if (biomeId >= 40 && biomeId <= 43) { // Java has multiple End dimensions that Bedrock doesn't recognize
|
|
||||||
biomeId = 9;
|
|
||||||
} else if (biomeId >= 170 && biomeId <= 173) { // 1.16 nether biomes. Dunno why it's like this :microjang:
|
|
||||||
biomeId += 8;
|
|
||||||
} else if (biomeId == 168) { // Bamboo jungle
|
|
||||||
biomeId = 48;
|
|
||||||
} else if (biomeId == 169) { // Bamboo jungle hills
|
|
||||||
biomeId = 49;
|
|
||||||
}
|
|
||||||
return biomeId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1 @@
|
|||||||
Subproject commit 8351b0f5bb6e9a1d614f84e18c91e82288c34bf6
|
Subproject commit f109d34a343da0ade6132661839b893859680d91
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren