3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-12-24 23:30:22 +01:00

Merge branch 'biomes'

Dieser Commit ist enthalten in:
Camotoy 2021-07-28 20:27:34 -04:00
Commit 7a99aa0ddf
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 7EEFB66FE798081F
10 geänderte Dateien mit 249 neuen und 96 gelöschten Zeilen

Datei anzeigen

@ -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.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@ -144,6 +142,7 @@ public class GeyserSession implements CommandSender {
private final PreferencesCache preferencesCache;
private final TagCache tagCache;
private WorldCache worldCache;
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
private final PlayerInventory playerInventory;
@ -189,6 +188,13 @@ public class GeyserSession implements CommandSender {
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
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.
* 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);
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
biomeDefinitionListPacket.setDefinitions(Registries.BIOMES.get());
biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get());
upstream.sendPacket(biomeDefinitionListPacket);
AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();

Datei anzeigen

@ -39,6 +39,7 @@ import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
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.DimensionUtils;
import org.geysermc.connector.utils.PluginMessageUtils;
@ -66,6 +67,7 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
}
session.setWorldName(packet.getWorldName());
BiomeTranslator.loadServerBiomes(session, packet.getDimensionCodec());
session.getTagCache().clear();
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();

Datei anzeigen

@ -40,7 +40,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
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;
@Translator(packet = ServerChunkDataPacket.class)
@ -101,7 +101,7 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
if (NEW_BIOME_WRITE) {
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
@ -110,7 +110,7 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
}
} 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
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now

Datei anzeigen

@ -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;
}
}

Datei anzeigen

@ -101,7 +101,7 @@ public class BlockStorage {
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);
if (index != -1) {
return index;

Datei anzeigen

@ -36,6 +36,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.PotionMixData;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
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.effect.Effect;
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.
*/
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}.

Datei anzeigen

@ -58,7 +58,7 @@ import java.util.function.Consumer;
* 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
* 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}.
*
* @param <M> the value being held by the registry

Datei anzeigen

@ -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;
}
}

Datei anzeigen

@ -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