diff --git a/Cargo.lock b/Cargo.lock index da6762a..e7c95ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,12 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -626,7 +632,9 @@ dependencies = [ "libc", "log", "minecraft-protocol", + "named-binary-tag", "pretty_env_logger", + "quartz_nbt", "rand 0.8.4", "rcon", "serde", @@ -634,6 +642,7 @@ dependencies = [ "thiserror", "tokio", "toml", + "uuid", "version-compare", "winapi", ] @@ -660,6 +669,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "md5" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d" + [[package]] name = "memchr" version = "2.4.1" @@ -669,7 +684,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "minecraft-protocol" version = "0.1.0" -source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=31041b8#31041b8fe2bc7e512d12476b958c1fe9e9077394" +source = "git+https://github.com/timvisee/rust-minecraft-protocol?branch=lazymc-v1_17_1#d26a525c7b29b61d2db64805181fb5471ea4317a" dependencies = [ "byteorder", "minecraft-protocol-derive", @@ -682,7 +697,7 @@ dependencies = [ [[package]] name = "minecraft-protocol-derive" version = "0.0.0" -source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=31041b8#31041b8fe2bc7e512d12476b958c1fe9e9077394" +source = "git+https://github.com/timvisee/rust-minecraft-protocol?branch=lazymc-v1_17_1#d26a525c7b29b61d2db64805181fb5471ea4317a" dependencies = [ "proc-macro2", "quote", @@ -723,9 +738,9 @@ dependencies = [ [[package]] name = "named-binary-tag" -version = "0.2.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d654702943d37d67f1491769ed46484c306f9b9d0d258348904bd63ffb101e8" +checksum = "523298fac63bd954f9a2e03b962b8a4a0e95110ad1b2fa3e0d7048660ffecec3" dependencies = [ "byteorder", "flate2", @@ -846,6 +861,30 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "quartz_nbt" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24532990479062a9c515987986225879bd115ccb97672b1fb56d788a5adb7d39" +dependencies = [ + "anyhow", + "byteorder", + "cesu8", + "flate2", + "quartz_nbt_macros", +] + +[[package]] +name = "quartz_nbt_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "289baa0c8a4d1f840d2de528a7f8c29e0e9af48b3018172b3edad4f716e8daed" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1251,6 +1290,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" dependencies = [ + "md5", "rand 0.6.5", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 0faab6f..557f6fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ derive_builder = "0.10" dotenv = "0.15" futures = { version = "0.3", default-features = false } log = "0.4" -minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "31041b8" } +minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", branch = "lazymc-v1_17_1" } pretty_env_logger = "0.4" rand = "0.8" serde = "1.0" @@ -44,6 +44,11 @@ version-compare = "0.1" # Feature: rcon rust_rcon = { package = "rcon", version = "0.5", optional = true } +# Feature: lobby +named-binary-tag = "0.6" +quartz_nbt = "0.2" +uuid = { version = "0.7", features = ["v3"] } + [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/res/dimension.snbt b/res/dimension.snbt new file mode 100644 index 0000000..24ac7ad --- /dev/null +++ b/res/dimension.snbt @@ -0,0 +1,18 @@ +{ + piglin_safe: 1b, + natural: 0b, + ambient_light: 0.0f, + fixed_time: 0, + infiniburn: "minecraft:infiniburn_overworld", + respawn_anchor_works: 0b, + has_skylight: 1b, + bed_works: 0b, + effects: "minecraft:the_end", + has_raids: 0b, + min_y: 0, + height: 256, + logical_height: 256, + coordinate_scale: 1.0d, + ultrawarm: 0b, + has_ceiling: 0b +} diff --git a/res/dimension_codec.snbt b/res/dimension_codec.snbt new file mode 100644 index 0000000..677d2e1 --- /dev/null +++ b/res/dimension_codec.snbt @@ -0,0 +1,2093 @@ +{ + "minecraft:dimension_type": { + type: "minecraft:dimension_type", + value: [ + { + name: "minecraft:overworld", + id: 0, + element: { + piglin_safe: 0b, + natural: 1b, + ambient_light: 0.0f, + infiniburn: "minecraft:infiniburn_overworld", + respawn_anchor_works: 0b, + has_skylight: 1b, + bed_works: 1b, + effects: "minecraft:overworld", + has_raids: 1b, + min_y: 0, + height: 256, + logical_height: 256, + coordinate_scale: 1.0d, + ultrawarm: 0b, + has_ceiling: 0b + } + }, + { + name: "minecraft:overworld_caves", + id: 1, + element: { + piglin_safe: 0b, + natural: 1b, + ambient_light: 0.0f, + infiniburn: "minecraft:infiniburn_overworld", + respawn_anchor_works: 0b, + has_skylight: 1b, + bed_works: 1b, + effects: "minecraft:overworld", + has_raids: 1b, + min_y: 0, + height: 256, + logical_height: 256, + coordinate_scale: 1.0d, + ultrawarm: 0b, + has_ceiling: 1b + } + }, + { + name: "minecraft:the_nether", + id: 2, + element: { + piglin_safe: 1b, + natural: 0b, + ambient_light: 0.1f, + infiniburn: "minecraft:infiniburn_nether", + respawn_anchor_works: 1b, + has_skylight: 0b, + bed_works: 0b, + effects: "minecraft:the_nether", + fixed_time: 18000L, + has_raids: 0b, + min_y: 0, + height: 256, + logical_height: 128, + coordinate_scale: 8.0d, + ultrawarm: 1b, + has_ceiling: 1b + } + }, + { + name: "minecraft:the_end", + id: 3, + element: { + piglin_safe: 0b, + natural: 0b, + ambient_light: 0.0f, + infiniburn: "minecraft:infiniburn_end", + respawn_anchor_works: 0b, + has_skylight: 0b, + bed_works: 0b, + effects: "minecraft:the_end", + fixed_time: 6000L, + has_raids: 1b, + min_y: 0, + height: 256, + logical_height: 256, + coordinate_scale: 1.0d, + ultrawarm: 0b, + has_ceiling: 0b + } + } + ] + }, + "minecraft:worldgen/biome": { + type: "minecraft:worldgen/biome", + value: [ + { + name: "minecraft:ocean", + id: 0, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.0f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:plains", + id: 1, + element: { + precipitation: "rain", + effects: { + sky_color: 7907327, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.125f, + temperature: 0.8f, + scale: 0.05f, + downfall: 0.4f, + category: "plains" + } + }, + { + name: "minecraft:desert", + id: 2, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.125f, + temperature: 2.0f, + scale: 0.05f, + downfall: 0.0f, + category: "desert" + } + }, + { + name: "minecraft:mountains", + id: 3, + element: { + precipitation: "rain", + effects: { + sky_color: 8233727, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.0f, + temperature: 0.2f, + scale: 0.5f, + downfall: 0.3f, + category: "extreme_hills" + } + }, + { + name: "minecraft:forest", + id: 4, + element: { + precipitation: "rain", + effects: { + sky_color: 7972607, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.7f, + scale: 0.2f, + downfall: 0.8f, + category: "forest" + } + }, + { + name: "minecraft:taiga", + id: 5, + element: { + precipitation: "rain", + effects: { + sky_color: 8233983, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.25f, + scale: 0.2f, + downfall: 0.8f, + category: "taiga" + } + }, + { + name: "minecraft:swamp", + id: 6, + element: { + precipitation: "rain", + effects: { + grass_color_modifier: "swamp", + sky_color: 7907327, + foliage_color: 6975545, + water_fog_color: 2302743, + fog_color: 12638463, + water_color: 6388580, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -0.2f, + temperature: 0.8f, + scale: 0.1f, + downfall: 0.9f, + category: "swamp" + } + }, + { + name: "minecraft:river", + id: 7, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -0.5f, + temperature: 0.5f, + scale: 0.0f, + downfall: 0.5f, + category: "river" + } + }, + { + name: "minecraft:nether_wastes", + id: 8, + element: { + precipitation: "none", + effects: { + music: { + replace_current_music: 0b, + max_delay: 24000, + sound: "minecraft:music.nether.nether_wastes", + min_delay: 12000 + }, + sky_color: 7254527, + ambient_sound: "minecraft:ambient.nether_wastes.loop", + additions_sound: { + sound: "minecraft:ambient.nether_wastes.additions", + tick_chance: 0.0111d + }, + water_fog_color: 329011, + fog_color: 3344392, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.nether_wastes.mood", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 2.0f, + scale: 0.2f, + downfall: 0.0f, + category: "nether" + } + }, + { + name: "minecraft:the_end", + id: 9, + element: { + precipitation: "none", + effects: { + sky_color: 0, + water_fog_color: 329011, + fog_color: 10518688, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.5f, + scale: 0.2f, + downfall: 0.5f, + category: "the_end" + } + }, + { + name: "minecraft:frozen_ocean", + id: 10, + element: { + precipitation: "snow", + effects: { + sky_color: 8364543, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 3750089, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.0f, + temperature: 0.0f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean", + temperature_modifier: "frozen" + } + }, + { + name: "minecraft:frozen_river", + id: 11, + element: { + precipitation: "snow", + effects: { + sky_color: 8364543, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 3750089, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -0.5f, + temperature: 0.0f, + scale: 0.0f, + downfall: 0.5f, + category: "river" + } + }, + { + name: "minecraft:snowy_tundra", + id: 12, + element: { + precipitation: "snow", + effects: { + sky_color: 8364543, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.125f, + temperature: 0.0f, + scale: 0.05f, + downfall: 0.5f, + category: "icy" + } + }, + { + name: "minecraft:snowy_mountains", + id: 13, + element: { + precipitation: "snow", + effects: { + sky_color: 8364543, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 0.0f, + scale: 0.3f, + downfall: 0.5f, + category: "icy" + } + }, + { + name: "minecraft:mushroom_fields", + id: 14, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.9f, + scale: 0.3f, + downfall: 1.0f, + category: "mushroom" + } + }, + { + name: "minecraft:mushroom_field_shore", + id: 15, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.0f, + temperature: 0.9f, + scale: 0.025f, + downfall: 1.0f, + category: "mushroom" + } + }, + { + name: "minecraft:beach", + id: 16, + element: { + precipitation: "rain", + effects: { + sky_color: 7907327, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.0f, + temperature: 0.8f, + scale: 0.025f, + downfall: 0.4f, + category: "beach" + } + }, + { + name: "minecraft:desert_hills", + id: 17, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 2.0f, + scale: 0.3f, + downfall: 0.0f, + category: "desert" + } + }, + { + name: "minecraft:wooded_hills", + id: 18, + element: { + precipitation: "rain", + effects: { + sky_color: 7972607, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 0.7f, + scale: 0.3f, + downfall: 0.8f, + category: "forest" + } + }, + { + name: "minecraft:taiga_hills", + id: 19, + element: { + precipitation: "rain", + effects: { + sky_color: 8233983, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 0.25f, + scale: 0.3f, + downfall: 0.8f, + category: "taiga" + } + }, + { + name: "minecraft:mountain_edge", + id: 20, + element: { + precipitation: "rain", + effects: { + sky_color: 8233727, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.8f, + temperature: 0.2f, + scale: 0.3f, + downfall: 0.3f, + category: "extreme_hills" + } + }, + { + name: "minecraft:jungle", + id: 21, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.95f, + scale: 0.2f, + downfall: 0.9f, + category: "jungle" + } + }, + { + name: "minecraft:jungle_hills", + id: 22, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 0.95f, + scale: 0.3f, + downfall: 0.9f, + category: "jungle" + } + }, + { + name: "minecraft:jungle_edge", + id: 23, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.95f, + scale: 0.2f, + downfall: 0.8f, + category: "jungle" + } + }, + { + name: "minecraft:deep_ocean", + id: 24, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.8f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:stone_shore", + id: 25, + element: { + precipitation: "rain", + effects: { + sky_color: 8233727, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.2f, + scale: 0.8f, + downfall: 0.3f, + category: "none" + } + }, + { + name: "minecraft:snowy_beach", + id: 26, + element: { + precipitation: "snow", + effects: { + sky_color: 8364543, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4020182, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.0f, + temperature: 0.05f, + scale: 0.025f, + downfall: 0.3f, + category: "beach" + } + }, + { + name: "minecraft:birch_forest", + id: 27, + element: { + precipitation: "rain", + effects: { + sky_color: 8037887, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.6f, + scale: 0.2f, + downfall: 0.6f, + category: "forest" + } + }, + { + name: "minecraft:birch_forest_hills", + id: 28, + element: { + precipitation: "rain", + effects: { + sky_color: 8037887, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 0.6f, + scale: 0.3f, + downfall: 0.6f, + category: "forest" + } + }, + { + name: "minecraft:dark_forest", + id: 29, + element: { + precipitation: "rain", + effects: { + grass_color_modifier: "dark_forest", + sky_color: 7972607, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.7f, + scale: 0.2f, + downfall: 0.8f, + category: "forest" + } + }, + { + name: "minecraft:snowy_taiga", + id: 30, + element: { + precipitation: "snow", + effects: { + sky_color: 8625919, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4020182, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: -0.5f, + scale: 0.2f, + downfall: 0.4f, + category: "taiga" + } + }, + { + name: "minecraft:snowy_taiga_hills", + id: 31, + element: { + precipitation: "snow", + effects: { + sky_color: 8625919, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4020182, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: -0.5f, + scale: 0.3f, + downfall: 0.4f, + category: "taiga" + } + }, + { + name: "minecraft:giant_tree_taiga", + id: 32, + element: { + precipitation: "rain", + effects: { + sky_color: 8168447, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.3f, + scale: 0.2f, + downfall: 0.8f, + category: "taiga" + } + }, + { + name: "minecraft:giant_tree_taiga_hills", + id: 33, + element: { + precipitation: "rain", + effects: { + sky_color: 8168447, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 0.3f, + scale: 0.3f, + downfall: 0.8f, + category: "taiga" + } + }, + { + name: "minecraft:wooded_mountains", + id: 34, + element: { + precipitation: "rain", + effects: { + sky_color: 8233727, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.0f, + temperature: 0.2f, + scale: 0.5f, + downfall: 0.3f, + category: "extreme_hills" + } + }, + { + name: "minecraft:savanna", + id: 35, + element: { + precipitation: "none", + effects: { + sky_color: 7711487, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.125f, + temperature: 1.2f, + scale: 0.05f, + downfall: 0.0f, + category: "savanna" + } + }, + { + name: "minecraft:savanna_plateau", + id: 36, + element: { + precipitation: "none", + effects: { + sky_color: 7776511, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.5f, + temperature: 1.0f, + scale: 0.025f, + downfall: 0.0f, + category: "savanna" + } + }, + { + name: "minecraft:badlands", + id: 37, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + grass_color: 9470285, + foliage_color: 10387789, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 2.0f, + scale: 0.2f, + downfall: 0.0f, + category: "mesa" + } + }, + { + name: "minecraft:wooded_badlands_plateau", + id: 38, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + grass_color: 9470285, + foliage_color: 10387789, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.5f, + temperature: 2.0f, + scale: 0.025f, + downfall: 0.0f, + category: "mesa" + } + }, + { + name: "minecraft:badlands_plateau", + id: 39, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + grass_color: 9470285, + foliage_color: 10387789, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.5f, + temperature: 2.0f, + scale: 0.025f, + downfall: 0.0f, + category: "mesa" + } + }, + { + name: "minecraft:small_end_islands", + id: 40, + element: { + precipitation: "none", + effects: { + sky_color: 0, + water_fog_color: 329011, + fog_color: 10518688, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.5f, + scale: 0.2f, + downfall: 0.5f, + category: "the_end" + } + }, + { + name: "minecraft:end_midlands", + id: 41, + element: { + precipitation: "none", + effects: { + sky_color: 0, + water_fog_color: 329011, + fog_color: 10518688, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.5f, + scale: 0.2f, + downfall: 0.5f, + category: "the_end" + } + }, + { + name: "minecraft:end_highlands", + id: 42, + element: { + precipitation: "none", + effects: { + sky_color: 0, + water_fog_color: 329011, + fog_color: 10518688, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.5f, + scale: 0.2f, + downfall: 0.5f, + category: "the_end" + } + }, + { + name: "minecraft:end_barrens", + id: 43, + element: { + precipitation: "none", + effects: { + sky_color: 0, + water_fog_color: 329011, + fog_color: 10518688, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.5f, + scale: 0.2f, + downfall: 0.5f, + category: "the_end" + } + }, + { + name: "minecraft:warm_ocean", + id: 44, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 270131, + fog_color: 12638463, + water_color: 4445678, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.0f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:lukewarm_ocean", + id: 45, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 267827, + fog_color: 12638463, + water_color: 4566514, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.0f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:cold_ocean", + id: 46, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4020182, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.0f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:deep_warm_ocean", + id: 47, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 270131, + fog_color: 12638463, + water_color: 4445678, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.8f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:deep_lukewarm_ocean", + id: 48, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 267827, + fog_color: 12638463, + water_color: 4566514, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.8f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:deep_cold_ocean", + id: 49, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4020182, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.8f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean" + } + }, + { + name: "minecraft:deep_frozen_ocean", + id: 50, + element: { + precipitation: "rain", + effects: { + sky_color: 8103167, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 3750089, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -1.8f, + temperature: 0.5f, + scale: 0.1f, + downfall: 0.5f, + category: "ocean", + temperature_modifier: "frozen" + } + }, + { + name: "minecraft:the_void", + id: 127, + element: { + precipitation: "none", + effects: { + sky_color: 8103167, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.5f, + scale: 0.2f, + downfall: 0.5f, + category: "none" + } + }, + { + name: "minecraft:sunflower_plains", + id: 129, + element: { + precipitation: "rain", + effects: { + sky_color: 7907327, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.125f, + temperature: 0.8f, + scale: 0.05f, + downfall: 0.4f, + category: "plains" + } + }, + { + name: "minecraft:desert_lakes", + id: 130, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.225f, + temperature: 2.0f, + scale: 0.25f, + downfall: 0.0f, + category: "desert" + } + }, + { + name: "minecraft:gravelly_mountains", + id: 131, + element: { + precipitation: "rain", + effects: { + sky_color: 8233727, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.0f, + temperature: 0.2f, + scale: 0.5f, + downfall: 0.3f, + category: "extreme_hills" + } + }, + { + name: "minecraft:flower_forest", + id: 132, + element: { + precipitation: "rain", + effects: { + sky_color: 7972607, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.7f, + scale: 0.4f, + downfall: 0.8f, + category: "forest" + } + }, + { + name: "minecraft:taiga_mountains", + id: 133, + element: { + precipitation: "rain", + effects: { + sky_color: 8233983, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.3f, + temperature: 0.25f, + scale: 0.4f, + downfall: 0.8f, + category: "taiga" + } + }, + { + name: "minecraft:swamp_hills", + id: 134, + element: { + precipitation: "rain", + effects: { + grass_color_modifier: "swamp", + sky_color: 7907327, + foliage_color: 6975545, + water_fog_color: 2302743, + fog_color: 12638463, + water_color: 6388580, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: -0.1f, + temperature: 0.8f, + scale: 0.3f, + downfall: 0.9f, + category: "swamp" + } + }, + { + name: "minecraft:ice_spikes", + id: 140, + element: { + precipitation: "snow", + effects: { + sky_color: 8364543, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.425f, + temperature: 0.0f, + scale: 0.45000002f, + downfall: 0.5f, + category: "icy" + } + }, + { + name: "minecraft:modified_jungle", + id: 149, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.95f, + scale: 0.4f, + downfall: 0.9f, + category: "jungle" + } + }, + { + name: "minecraft:modified_jungle_edge", + id: 151, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.95f, + scale: 0.4f, + downfall: 0.8f, + category: "jungle" + } + }, + { + name: "minecraft:tall_birch_forest", + id: 155, + element: { + precipitation: "rain", + effects: { + sky_color: 8037887, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.6f, + scale: 0.4f, + downfall: 0.6f, + category: "forest" + } + }, + { + name: "minecraft:tall_birch_hills", + id: 156, + element: { + precipitation: "rain", + effects: { + sky_color: 8037887, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.55f, + temperature: 0.6f, + scale: 0.5f, + downfall: 0.6f, + category: "forest" + } + }, + { + name: "minecraft:dark_forest_hills", + id: 157, + element: { + precipitation: "rain", + effects: { + grass_color_modifier: "dark_forest", + sky_color: 7972607, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.7f, + scale: 0.4f, + downfall: 0.8f, + category: "forest" + } + }, + { + name: "minecraft:snowy_taiga_mountains", + id: 158, + element: { + precipitation: "snow", + effects: { + sky_color: 8625919, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4020182, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.3f, + temperature: -0.5f, + scale: 0.4f, + downfall: 0.4f, + category: "taiga" + } + }, + { + name: "minecraft:giant_spruce_taiga", + id: 160, + element: { + precipitation: "rain", + effects: { + sky_color: 8233983, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.25f, + scale: 0.2f, + downfall: 0.8f, + category: "taiga" + } + }, + { + name: "minecraft:giant_spruce_taiga_hills", + id: 161, + element: { + precipitation: "rain", + effects: { + sky_color: 8233983, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.2f, + temperature: 0.25f, + scale: 0.2f, + downfall: 0.8f, + category: "taiga" + } + }, + { + name: "minecraft:modified_gravelly_mountains", + id: 162, + element: { + precipitation: "rain", + effects: { + sky_color: 8233727, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.0f, + temperature: 0.2f, + scale: 0.5f, + downfall: 0.3f, + category: "extreme_hills" + } + }, + { + name: "minecraft:shattered_savanna", + id: 163, + element: { + precipitation: "none", + effects: { + sky_color: 7776767, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.3625f, + temperature: 1.1f, + scale: 1.225f, + downfall: 0.0f, + category: "savanna" + } + }, + { + name: "minecraft:shattered_savanna_plateau", + id: 164, + element: { + precipitation: "none", + effects: { + sky_color: 7776511, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 1.05f, + temperature: 1.0f, + scale: 1.2125001f, + downfall: 0.0f, + category: "savanna" + } + }, + { + name: "minecraft:eroded_badlands", + id: 165, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + grass_color: 9470285, + foliage_color: 10387789, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 2.0f, + scale: 0.2f, + downfall: 0.0f, + category: "mesa" + } + }, + { + name: "minecraft:modified_wooded_badlands_plateau", + id: 166, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + grass_color: 9470285, + foliage_color: 10387789, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 2.0f, + scale: 0.3f, + downfall: 0.0f, + category: "mesa" + } + }, + { + name: "minecraft:modified_badlands_plateau", + id: 167, + element: { + precipitation: "none", + effects: { + sky_color: 7254527, + grass_color: 9470285, + foliage_color: 10387789, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 2.0f, + scale: 0.3f, + downfall: 0.0f, + category: "mesa" + } + }, + { + name: "minecraft:bamboo_jungle", + id: 168, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 0.95f, + scale: 0.2f, + downfall: 0.9f, + category: "jungle" + } + }, + { + name: "minecraft:bamboo_jungle_hills", + id: 169, + element: { + precipitation: "rain", + effects: { + sky_color: 7842047, + water_fog_color: 329011, + fog_color: 12638463, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.cave", + block_search_extent: 8 + } + }, + depth: 0.45f, + temperature: 0.95f, + scale: 0.3f, + downfall: 0.9f, + category: "jungle" + } + }, + { + name: "minecraft:soul_sand_valley", + id: 170, + element: { + precipitation: "none", + effects: { + music: { + replace_current_music: 0b, + max_delay: 24000, + sound: "minecraft:music.nether.soul_sand_valley", + min_delay: 12000 + }, + sky_color: 7254527, + ambient_sound: "minecraft:ambient.soul_sand_valley.loop", + additions_sound: { + sound: "minecraft:ambient.soul_sand_valley.additions", + tick_chance: 0.0111d + }, + particle: { + probability: 0.00625f, + options: { + type: "minecraft:ash" + } + }, + water_fog_color: 329011, + fog_color: 1787717, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.soul_sand_valley.mood", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 2.0f, + scale: 0.2f, + downfall: 0.0f, + category: "nether" + } + }, + { + name: "minecraft:crimson_forest", + id: 171, + element: { + precipitation: "none", + effects: { + music: { + replace_current_music: 0b, + max_delay: 24000, + sound: "minecraft:music.nether.crimson_forest", + min_delay: 12000 + }, + sky_color: 7254527, + ambient_sound: "minecraft:ambient.crimson_forest.loop", + additions_sound: { + sound: "minecraft:ambient.crimson_forest.additions", + tick_chance: 0.0111d + }, + particle: { + probability: 0.025f, + options: { + type: "minecraft:crimson_spore" + } + }, + water_fog_color: 329011, + fog_color: 3343107, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.crimson_forest.mood", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 2.0f, + scale: 0.2f, + downfall: 0.0f, + category: "nether" + } + }, + { + name: "minecraft:warped_forest", + id: 172, + element: { + precipitation: "none", + effects: { + music: { + replace_current_music: 0b, + max_delay: 24000, + sound: "minecraft:music.nether.warped_forest", + min_delay: 12000 + }, + sky_color: 7254527, + ambient_sound: "minecraft:ambient.warped_forest.loop", + additions_sound: { + sound: "minecraft:ambient.warped_forest.additions", + tick_chance: 0.0111d + }, + particle: { + probability: 0.01428f, + options: { + type: "minecraft:warped_spore" + } + }, + water_fog_color: 329011, + fog_color: 1705242, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.warped_forest.mood", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 2.0f, + scale: 0.2f, + downfall: 0.0f, + category: "nether" + } + }, + { + name: "minecraft:basalt_deltas", + id: 173, + element: { + precipitation: "none", + effects: { + music: { + replace_current_music: 0b, + max_delay: 24000, + sound: "minecraft:music.nether.basalt_deltas", + min_delay: 12000 + }, + sky_color: 7254527, + ambient_sound: "minecraft:ambient.basalt_deltas.loop", + additions_sound: { + sound: "minecraft:ambient.basalt_deltas.additions", + tick_chance: 0.0111d + }, + particle: { + probability: 0.118093334f, + options: { + type: "minecraft:white_ash" + } + }, + water_fog_color: 4341314, + fog_color: 6840176, + water_color: 4159204, + mood_sound: { + tick_delay: 6000, + offset: 2.0d, + sound: "minecraft:ambient.basalt_deltas.mood", + block_search_extent: 8 + } + }, + depth: 0.1f, + temperature: 2.0f, + scale: 0.2f, + downfall: 0.0f, + category: "nether" + } + } + ] + } +} diff --git a/src/lobby.rs b/src/lobby.rs new file mode 100644 index 0000000..3e2e2ec --- /dev/null +++ b/src/lobby.rs @@ -0,0 +1,426 @@ +// TODO: remove this before feature release! +#![allow(unused)] + +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use bytes::BytesMut; +use minecraft_protocol::data::chat::{Message, Payload}; +use minecraft_protocol::data::server_status::*; +use minecraft_protocol::decoder::Decoder; +use minecraft_protocol::encoder::Encoder; +use minecraft_protocol::version::v1_14_4::game::{GameMode, MessagePosition}; +use minecraft_protocol::version::v1_14_4::handshake::Handshake; +use minecraft_protocol::version::v1_14_4::login::{LoginDisconnect, LoginStart, LoginSuccess}; +use minecraft_protocol::version::v1_14_4::status::StatusResponse; +use minecraft_protocol::version::v1_17_1::game::{ + ChunkData, ClientBoundChatMessage, ClientBoundKeepAlive, JoinGame, PlayerPositionAndLook, + SetTitleSubtitle, SetTitleText, SetTitleTimes, SpawnPosition, TimeUpdate, +}; +use nbt::CompoundTag; +use tokio::io::{self, AsyncWriteExt}; +use tokio::net::tcp::WriteHalf; +use tokio::net::TcpStream; +use tokio::time; +use uuid::Uuid; + +use crate::config::*; +use crate::proto::{self, Client, ClientState, RawPacket}; +use crate::server::{self, Server, State}; +use crate::service; + +// TODO: remove this before releasing feature +pub const USE_LOBBY: bool = true; +pub const DONT_START_SERVER: bool = true; +const STARTING_BANNER: &str = "§2 Server is starting..."; +const STARTING_BANNER_SUB: &str = "§7⌛ Please wait..."; + +// TODO: do not drop error here, return Box +pub async fn serve( + client: Client, + mut inbound: TcpStream, + config: Arc, + server: Arc, + queue: BytesMut, +) -> Result<(), ()> { + let (mut reader, mut writer) = inbound.split(); + + // TODO: note this assumes the first receiving packet (over queue) is login start + // TODO: assert client is in login mode! + + // Incoming buffer and packet holding queue + let mut buf = queue; + let mut hold_queue = BytesMut::new(); + + loop { + // Read packet from stream + let (packet, raw) = match proto::read_packet(&mut buf, &mut reader).await { + Ok(Some(packet)) => packet, + Ok(None) => break, + Err(_) => { + error!(target: "lazymc", "Closing connection, error occurred"); + break; + } + }; + + // Grab client state + let client_state = client.state(); + + // Hijack login start + if client_state == ClientState::Login && packet.id == proto::LOGIN_PACKET_ID_LOGIN_START { + // Try to get login username + let login_start = LoginStart::decode(&mut packet.data.as_slice()).map_err(|_| ())?; + + // TODO: remove debug message + debug!(target: "LOBBY", "Login {:?}", login_start.name); + + // Respond with login success + let packet = LoginSuccess { + // TODO: use correct username here + uuid: Uuid::new_v3( + &Uuid::new_v3(&Uuid::NAMESPACE_OID, b"OfflinePlayer"), + login_start.name.as_bytes(), + ), + username: login_start.name, + // uuid: Uuid::parse_str("35ee313b-d89a-41b8-b25e-d32e8aff0389").unwrap(), + // username: "Username".into(), + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::LOGIN_PACKET_ID_LOGIN_SUCCESS, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + // Update client state to play + client.set_state(ClientState::Play); + + // TODO: remove debug message + debug!(target: "LOBBY", "Sent login success, moving to play state"); + + // TODO: handle errors here + play_packets(&mut writer).await; + + debug!(target: "LOBBY", "Done with playing packets, client disconnect?"); + + break; + } + + if client_state == ClientState::Play + && packet.id == proto::packets::play::SERVER_CLIENT_SETTINGS + { + debug!(target: "LOBBY", "Ignoring client settings packet"); + continue; + } + + if client_state == ClientState::Play + && packet.id == proto::packets::play::SERVER_PLUGIN_MESSAGE + { + debug!(target: "LOBBY", "Ignoring plugin message packet"); + continue; + } + + if client_state == ClientState::Play + && packet.id == proto::packets::play::SERVER_PLAYER_POS_ROT + { + debug!(target: "LOBBY", "Ignoring player pos rot packet"); + continue; + } + + if client_state == ClientState::Play && packet.id == proto::packets::play::SERVER_PLAYER_POS + { + debug!(target: "LOBBY", "Ignoring player pos packet"); + continue; + } + + // Show unhandled packet warning + debug!(target: "lazymc", "Received unhandled packet:"); + debug!(target: "lazymc", "- State: {:?}", client_state); + debug!(target: "lazymc", "- Packet ID: 0x{:02X} ({})", packet.id, packet.id); + } + + // Gracefully close connection + match writer.shutdown().await { + Ok(_) => {} + Err(err) if err.kind() == io::ErrorKind::NotConnected => {} + Err(_) => return Err(()), + } + + Ok(()) +} + +/// Kick client with a message. +/// +/// Should close connection afterwards. +async fn kick(msg: &str, writer: &mut WriteHalf<'_>) -> Result<(), ()> { + let packet = LoginDisconnect { + reason: Message::new(Payload::text(msg)), + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::LOGIN_PACKET_ID_DISCONNECT, data).encode()?; + writer.write_all(&response).await.map_err(|_| ()) +} + +async fn play_packets(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + debug!(target: "LOBBY", "Send play packets"); + + // See: https://wiki.vg/Protocol_FAQ#What.27s_the_normal_login_sequence_for_a_client.3F + + // Send game join + send_join_game(writer).await?; + + // TODO: send brand plugin message + + // After this, we receive: + // - PLAY_PAKCET_ID_CLIENT_SETTINGS + // - PLAY_PAKCET_ID_PLUGIN_MESSAGE + // - PLAY_PAKCET_ID_PLAYER_POS_ROT + // - PLAY_PAKCET_ID_PLAYER_POS ... + + // TODO: send Update View Position ? + // TODO: send Update View Distance ? + + // Send chunk data + // TODO: send_chunk_data(writer).await?; + + // TODO: probably not required + send_spawn_pos(writer).await?; + + // Send player location, disables download terrain screen + send_player_pos(writer).await?; + + // TODO: send Update View Position + // TODO: send Spawn Position + // TODO: send Position and Look (one more time) + + // Send time update + send_time_update(writer).await?; + + // // Keep sending keep alive packets + send_keep_alive_loop(writer).await?; + + Ok(()) +} + +async fn send_join_game(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + // // TODO: use proper values here! + // let packet = JoinGame { + // // entity_id: 0, + // // game_mode: GameMode::Spectator, + // entity_id: 27, + // game_mode: GameMode::Hardcore, + // dimension: 23, + // max_players: 100, + // level_type: String::from("default"), + // view_distance: 10, + // reduced_debug_info: true, + // }; + + // TODO: use proper values here! + let packet = JoinGame { + entity_id: 0x6d, + hardcore: false, + game_mode: 3, + previous_game_mode: -1i8 as u8, // use -1i8 as u8? + world_names: vec![ + "minecraft:overworld".into(), + "minecraft:the_nether".into(), + "minecraft:the_end".into(), + ], + dimension_codec: snbt_to_compound_tag(include_str!("../res/dimension_codec.snbt")), + dimension: snbt_to_compound_tag(include_str!("../res/dimension.snbt")), + world_name: "minecraft:overworld".into(), + hashed_seed: 0, + max_players: 20, + view_distance: 10, + reduced_debug_info: true, + enable_respawn_screen: false, + is_debug: false, + is_flat: false, + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::packets::play::CLIENT_JOIN_GAME, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + Ok(()) +} + +// // TODO: this is possibly broken? +// async fn send_chunk_data(writer: &mut WriteHalf<'_>) -> Result<(), ()> { +// // Send player location, disables download terrain screen +// let packet = ChunkData { +// x: 0, +// z: 0, +// primary_mask: Vec::new(), +// heightmaps: CompoundTag::named("HeightMaps"), +// biomes: Vec::new(), +// data_size: 0, +// data: Vec::new(), +// block_entities_size: 0, +// block_entities: Vec::new(), +// // primary_mask: 65535, +// // heights: CompoundTag::named("HeightMaps"), +// // data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], +// // tiles: vec![CompoundTag::named("TileEntity")], +// }; + +// let mut data = Vec::new(); +// packet.encode(&mut data).map_err(|_| ())?; + +// let response = RawPacket::new(proto::CLIENT_CHUNK_DATA, data).encode()?; +// writer.write_all(&response).await.map_err(|_| ())?; + +// Ok(()) +// } + +async fn send_spawn_pos(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + let packet = SpawnPosition { + position: 0, + angle: 0.0, + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::packets::play::CLIENT_SPAWN_POS, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + Ok(()) +} + +async fn send_player_pos(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + // Send player location, disables download terrain screen + let packet = PlayerPositionAndLook { + x: 0.0, + y: 0.0, + z: 0.0, + yaw: 0.0, + pitch: 90.0, + flags: 0b00000000, + teleport_id: 0, + dismount_vehicle: true, + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::packets::play::CLIENT_PLAYER_POS_LOOK, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + Ok(()) +} + +async fn send_time_update(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + const MC_TIME_NOON: i64 = 6000; + + // Send player location, disables download terrain screen + let packet = TimeUpdate { + world_age: MC_TIME_NOON, + time_of_day: MC_TIME_NOON, + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::packets::play::CLIENT_TIME_UPDATE, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + Ok(()) +} + +async fn send_keep_alive(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + // Send player location, disables download terrain screen + // TODO: keep picking random ID! + let packet = ClientBoundKeepAlive { id: 0 }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::packets::play::CLIENT_KEEP_ALIVE, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + // TODO: require to receive correct keepalive! + + Ok(()) +} + +async fn send_keep_alive_loop(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + // TODO: use interval of 10 sec? + let mut poll_interval = time::interval(Duration::from_secs(10)); + + loop { + // TODO: wait for start signal over channel instead of polling + poll_interval.tick().await; + + debug!(target: "LOBBY", "Sending keep-alive to client"); + send_keep_alive(writer).await?; + + send_title(writer).await?; + } + + Ok(()) +} + +async fn send_title(writer: &mut WriteHalf<'_>) -> Result<(), ()> { + // Set title + let packet = SetTitleText { + text: Message::new(Payload::text(STARTING_BANNER)), + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_TEXT, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + // Set subtitle + let packet = SetTitleSubtitle { + text: Message::new(Payload::text(STARTING_BANNER_SUB)), + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = + RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_SUBTITLE, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + // Set times + let packet = SetTitleTimes { + fade_in: 0, + stay: i32::MAX, + fade_out: 0, + }; + + let mut data = Vec::new(); + packet.encode(&mut data).map_err(|_| ())?; + + let response = RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_TIMES, data).encode()?; + writer.write_all(&response).await.map_err(|_| ())?; + + Ok(()) +} + +/// Read NBT CompoundTag from SNBT. +fn snbt_to_compound_tag(data: &str) -> CompoundTag { + use nbt::decode::read_compound_tag; + use quartz_nbt::io::{self, Flavor}; + use quartz_nbt::snbt; + use std::io::Cursor; + + // Parse SNBT data + let compound = snbt::parse(data).expect("failed to parse SNBT"); + + // Encode to binary + let mut binary = Vec::new(); + io::write_nbt(&mut binary, None, &compound, Flavor::Uncompressed); + + // Parse binary with usable NBT create + read_compound_tag(&mut &*binary).unwrap() +} diff --git a/src/main.rs b/src/main.rs index 3b675e3..1faac2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ extern crate log; pub(crate) mod action; pub(crate) mod cli; pub(crate) mod config; +pub(crate) mod lobby; pub(crate) mod mc; pub(crate) mod monitor; pub(crate) mod os; diff --git a/src/proto.rs b/src/proto.rs index c4fa050..d0a8b9a 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -30,6 +30,31 @@ pub const STATUS_PACKET_ID_PING: i32 = 1; /// Login state, login start packet ID. pub const LOGIN_PACKET_ID_LOGIN_START: i32 = 0; +/// Login state, disconnect packet ID. +pub const LOGIN_PACKET_ID_DISCONNECT: i32 = 0; + +/// Login state, login success packet ID. +pub const LOGIN_PACKET_ID_LOGIN_SUCCESS: i32 = 2; + +pub mod packets { + pub mod play { + pub const CLIENT_JOIN_GAME: i32 = 0x26; + pub const SERVER_CLIENT_SETTINGS: i32 = 0x05; + pub const SERVER_PLUGIN_MESSAGE: i32 = 0x0A; + pub const SERVER_PLAYER_POS_ROT: i32 = 0x12; + pub const SERVER_PLAYER_POS: i32 = 0x11; + pub const CLIENT_KEEP_ALIVE: i32 = 0x21; + pub const CLIENT_CHUNK_DATA: i32 = 0x22; + pub const CLIENT_PLAYER_POS_LOOK: i32 = 0x38; + pub const CLIENT_SET_TITLE_TEXT: i32 = 0x59; + pub const CLIENT_SET_TITLE_SUBTITLE: i32 = 0x57; + pub const CLIENT_SET_TITLE_TIMES: i32 = 0x5A; + pub const CLIENT_TIME_UPDATE: i32 = 0x58; + pub const CLIENT_CHAT_MSG: i32 = 0x0F; + pub const CLIENT_SPAWN_POS: i32 = 0x4B; + } +} + /// Client state. /// /// Note: this does not keep track of compression/encryption states because packets are never @@ -66,6 +91,9 @@ pub enum ClientState { /// State to login to server. Login, + + /// State to play on the server. + Play, } impl ClientState { @@ -85,6 +113,7 @@ impl ClientState { Self::Handshake => 0, Self::Status => 1, Self::Login => 2, + Self::Play => -1, } } } diff --git a/src/status.rs b/src/status.rs index 7d257ef..a708b53 100644 --- a/src/status.rs +++ b/src/status.rs @@ -2,7 +2,6 @@ use std::ops::Deref; use std::sync::Arc; use std::time::Duration; -use crate::server::State; use bytes::BytesMut; use minecraft_protocol::data::chat::{Message, Payload}; use minecraft_protocol::data::server_status::*; @@ -17,8 +16,9 @@ use tokio::net::TcpStream; use tokio::time; use crate::config::*; +use crate::lobby; use crate::proto::{self, Client, ClientState, RawPacket}; -use crate::server::{self, Server}; +use crate::server::{self, Server, State}; use crate::service; /// Proxy the given inbound stream to a target address. @@ -31,7 +31,7 @@ pub async fn serve( ) -> Result<(), ()> { let (mut reader, mut writer) = inbound.split(); - // Incoming buffer + // Incoming buffer and packet holding queue let mut buf = BytesMut::new(); // Remember inbound packets, used for client holding and forwarding @@ -120,8 +120,26 @@ pub async fn serve( break; } - // Start server if not starting yet - Server::start(config.clone(), server.clone(), username).await; + if !lobby::DONT_START_SERVER { + // Start server if not starting yet + Server::start(config.clone(), server.clone(), username).await; + } + + // Lobby mode + if lobby::USE_LOBBY { + // // Hold login packet and remaining read bytes + // hold_queue.extend(raw); + // hold_queue.extend(buf.split_off(0)); + + // Build queue with login packet and any additionally received + let mut queue = BytesMut::with_capacity(raw.len() + buf.len()); + queue.extend(raw); + queue.extend(buf.split_off(0)); + + // Start lobby + lobby::serve(client, inbound, config, server, queue).await?; + return Ok(()); + } // Use join occupy methods for method in &config.join.methods { @@ -283,7 +301,7 @@ async fn kick(msg: &str, writer: &mut WriteHalf<'_>) -> Result<(), ()> { let mut data = Vec::new(); packet.encode(&mut data).map_err(|_| ())?; - let response = RawPacket::new(0, data).encode()?; + let response = RawPacket::new(proto::LOGIN_PACKET_ID_DISCONNECT, data).encode()?; writer.write_all(&response).await.map_err(|_| ()) }