From 864c1f50f01eee0efc65096fecd13e262e3eb512 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Sun, 6 Jun 2021 21:31:58 +0200 Subject: [PATCH] bump pixlyzer, physics: jump and velocity multipliers (soul sand, honey), climbing --- .../minosoft/data/entities/entities/Entity.kt | 17 ++- .../data/mappings/blocks/DefaultBlocks.kt | 14 +++ .../data/mappings/blocks/types/Block.kt | 3 + .../enchantment/DefaultEnchantments.kt | 1 + .../data/mappings/items/DefaultItems.kt | 20 +++ .../minosoft/data/player/LocalPlayerEntity.kt | 119 +++++++++++++++--- .../minosoft/data/tags/DefaultBlockTags.kt | 20 +++ .../input/camera/EntityPositionInfo.kt | 3 + .../protocol/packets/s2c/play/TagsS2CP.kt | 2 +- .../minosoft/mapping/pixlyzer_index.json | 2 +- 10 files changed, 179 insertions(+), 22 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/data/mappings/items/DefaultItems.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/tags/DefaultBlockTags.kt diff --git a/src/main/java/de/bixilon/minosoft/data/entities/entities/Entity.kt b/src/main/java/de/bixilon/minosoft/data/entities/entities/Entity.kt index 4d3c9bd20..970b4d044 100644 --- a/src/main/java/de/bixilon/minosoft/data/entities/entities/Entity.kt +++ b/src/main/java/de/bixilon/minosoft/data/entities/entities/Entity.kt @@ -24,6 +24,7 @@ import de.bixilon.minosoft.data.mappings.effects.StatusEffect import de.bixilon.minosoft.data.mappings.effects.attributes.StatusEffectAttribute import de.bixilon.minosoft.data.mappings.effects.attributes.StatusEffectAttributeInstance import de.bixilon.minosoft.data.mappings.effects.attributes.StatusEffectOperations +import de.bixilon.minosoft.data.mappings.enchantment.Enchantment import de.bixilon.minosoft.data.mappings.entities.EntityType import de.bixilon.minosoft.data.physics.PhysicsEntity import de.bixilon.minosoft.data.text.ChatComponent @@ -36,6 +37,7 @@ import de.bixilon.minosoft.protocol.network.connection.PlayConnection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.util.KUtil.synchronizedMapOf import de.bixilon.minosoft.util.KUtil.synchronizedSetOf +import de.bixilon.minosoft.util.KUtil.toSynchronizedMap import glm_.vec2.Vec2 import glm_.vec3.Vec3 import glm_.vec3.Vec3d @@ -65,7 +67,7 @@ abstract class Entity( var passengers: MutableSet = synchronizedSetOf() override var velocity: Vec3d = Vec3d.EMPTY - var velocityMultiplier = Vec3d.EMPTY + var movementMultiplier = Vec3d.EMPTY // ToDo: Used in cobwebs, etc protected open val hasCollisions = true @@ -305,6 +307,19 @@ abstract class Entity( override val aabb: AABB get() = defaultAABB + position + fun getEquipmentEnchant(enchantment: Enchantment?): Int { + enchantment ?: return 0 + var maxLevel = 0 + for ((slot, equipment) in this.equipment.toSynchronizedMap()) { + equipment.enchantments[enchantment]?.let { + if (it > maxLevel) { + maxLevel = it + } + } + } + return maxLevel + } + companion object { private const val HITBOX_MARGIN = 1e-5f diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/DefaultBlocks.kt b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/DefaultBlocks.kt index 6d8018fde..e2257a80c 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/DefaultBlocks.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/DefaultBlocks.kt @@ -16,7 +16,21 @@ package de.bixilon.minosoft.data.mappings.blocks import de.bixilon.minosoft.util.KUtil.asResourceLocation object DefaultBlocks { + val SCAFFOLDING = "minecraft:scaffolding".asResourceLocation() + val LADDER = "minecraft:ladder".asResourceLocation() + val VINE = "minecraft:vine".asResourceLocation() + val WEEPING_VINES = "minecraft:weeping_vines".asResourceLocation() + val WEEPING_VINES_PLANT = "minecraft:weeping_vines_plant".asResourceLocation() + val TWISTING_VINES = "minecraft:twisting_vines".asResourceLocation() + val TWISTING_VINES_PLANT = "minecraft:twisting_vines_plant".asResourceLocation() + val CAVE_VINES = "minecraft:cave_vines".asResourceLocation() + val CAVE_VINES_PLANT = "minecraft:cave_vines_plant".asResourceLocation() + + val POWDER_SNOW = "minecraft:powder_snow".asResourceLocation() val GRASS_BLOCK = "minecraft:grass_block".asResourceLocation() val MOVING_PISTON = "minecraft:moving_piston".asResourceLocation() val COBWEB = "minecraft:cobweb".asResourceLocation() + + val WATER = "minecraft:water".asResourceLocation() + val BUBBLE_COLUMN = "minecraft:bubble_column".asResourceLocation() } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/types/Block.kt b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/types/Block.kt index 59c49cc30..35df25c9a 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/types/Block.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/types/Block.kt @@ -59,6 +59,9 @@ open class Block( open lateinit var item: Item protected set open lateinit var properties: Map> + open val friction = data["friction"]?.asDouble ?: 0.6 + open val velocityMultiplier = data["velocity_multiplier"]?.asDouble ?: 1.0 // ToDo: They exist since ~1.15 + open val jumpVelocityMultiplier = data["jump_velocity_multiplier"]?.asDouble ?: 1.0 override fun postInit(registries: Registries) { item = registries.itemRegistry[itemId] diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/enchantment/DefaultEnchantments.kt b/src/main/java/de/bixilon/minosoft/data/mappings/enchantment/DefaultEnchantments.kt index 2d3ee4ebb..a4147abd9 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/enchantment/DefaultEnchantments.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/enchantment/DefaultEnchantments.kt @@ -16,6 +16,7 @@ package de.bixilon.minosoft.data.mappings.enchantment import de.bixilon.minosoft.util.KUtil.asResourceLocation object DefaultEnchantments { + val SOUL_SPEED = "minecraft:soul_speed".asResourceLocation() val UNBREAKING = "minecraft:unbreaking".asResourceLocation() val EFFICIENCY = "minecraft:efficiency".asResourceLocation() } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/items/DefaultItems.kt b/src/main/java/de/bixilon/minosoft/data/mappings/items/DefaultItems.kt new file mode 100644 index 000000000..854881dc6 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/mappings/items/DefaultItems.kt @@ -0,0 +1,20 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.mappings.items + +import de.bixilon.minosoft.util.KUtil.asResourceLocation + +object DefaultItems { + val LEATHER_BOOTS = "minecraft:leather_boots".asResourceLocation() +} diff --git a/src/main/java/de/bixilon/minosoft/data/player/LocalPlayerEntity.kt b/src/main/java/de/bixilon/minosoft/data/player/LocalPlayerEntity.kt index c27ab7888..966c2f963 100644 --- a/src/main/java/de/bixilon/minosoft/data/player/LocalPlayerEntity.kt +++ b/src/main/java/de/bixilon/minosoft/data/player/LocalPlayerEntity.kt @@ -19,14 +19,21 @@ import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.data.entities.EntityRotation import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity import de.bixilon.minosoft.data.entities.entities.player.RemotePlayerEntity +import de.bixilon.minosoft.data.inventory.InventorySlots +import de.bixilon.minosoft.data.mappings.blocks.DefaultBlocks +import de.bixilon.minosoft.data.mappings.blocks.types.Block import de.bixilon.minosoft.data.mappings.effects.DefaultStatusEffects import de.bixilon.minosoft.data.mappings.effects.attributes.DefaultStatusEffectAttributeNames import de.bixilon.minosoft.data.mappings.effects.attributes.DefaultStatusEffectAttributes import de.bixilon.minosoft.data.mappings.effects.attributes.StatusEffectAttributeInstance +import de.bixilon.minosoft.data.mappings.enchantment.DefaultEnchantments +import de.bixilon.minosoft.data.mappings.items.DefaultItems import de.bixilon.minosoft.data.mappings.items.Item import de.bixilon.minosoft.data.mappings.other.containers.Container import de.bixilon.minosoft.data.mappings.other.containers.PlayerInventory import de.bixilon.minosoft.data.physics.PhysicsConstants +import de.bixilon.minosoft.data.tags.DefaultBlockTags +import de.bixilon.minosoft.data.tags.Tag import de.bixilon.minosoft.gui.rendering.input.camera.MovementInput import de.bixilon.minosoft.gui.rendering.util.VecUtil import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY @@ -35,8 +42,11 @@ import de.bixilon.minosoft.gui.rendering.util.VecUtil.clearZero import de.bixilon.minosoft.gui.rendering.util.VecUtil.empty import de.bixilon.minosoft.protocol.network.connection.PlayConnection import de.bixilon.minosoft.protocol.packets.c2s.play.* +import de.bixilon.minosoft.protocol.packets.s2c.play.TagsS2CP import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import de.bixilon.minosoft.util.KUtil.asResourceLocation import de.bixilon.minosoft.util.KUtil.decide +import de.bixilon.minosoft.util.KUtil.nullCast import de.bixilon.minosoft.util.KUtil.synchronizedMapOf import de.bixilon.minosoft.util.MMath import glm_.func.cos @@ -45,9 +55,8 @@ import glm_.func.sin import glm_.vec3.Vec3 import glm_.vec3.Vec3d import glm_.vec3.Vec3i -import kotlin.math.cos +import kotlin.math.max import kotlin.math.pow -import kotlin.math.sin class LocalPlayerEntity( account: Account, @@ -127,12 +136,57 @@ class LocalPlayerEntity( field = value } - val canSwimInFluids: Boolean + private val canSwimInFluids: Boolean get() = !baseAbilities.isFlying override val isSneaking: Boolean get() = input.sneaking + private val isClimbing: Boolean + get() { + if (gamemode == Gamemodes.SPECTATOR) { + return false + } + val blockState = connection.world[positionInfo.blockPosition] ?: return false + + connection.tags[TagsS2CP.BLOCK_TAG_RESOURCE_LOCATION]?.get(CLIMBABLE_TAG)?.nullCast>()?.let { + return it.entries.contains(blockState.block) + } + return DefaultBlockTags.CLIMBABLE.contains(blockState.block.resourceLocation) + } + + private val velocityMultiplier: Double + get() { + if (isFlyingWithElytra || baseAbilities.isFlying) { + return 1.0 + } + val onSoulSpeedBlock = connection.tags[TagsS2CP.BLOCK_TAG_RESOURCE_LOCATION]?.get(SOUL_SPEED_BLOCKS)?.nullCast>()?.entries?.contains(connection.world[positionInfo.velocityPosition]?.block) ?: false + + if (onSoulSpeedBlock && getEquipmentEnchant(connection.registries.enchantmentRegistry[DefaultEnchantments.SOUL_SPEED]) > 0) { + // ToDo + return 1.0 + } + + val blockStateBelow = connection.world[positionInfo.blockPosition] ?: return 1.0 + + if (blockStateBelow.block.resourceLocation == DefaultBlocks.WATER || blockStateBelow.block.resourceLocation == DefaultBlocks.BUBBLE_COLUMN) { + if (blockStateBelow.block.velocityMultiplier == 1.0) { + return connection.world[positionInfo.blockPosition]?.block?.velocityMultiplier ?: 1.0 + } + } + return blockStateBelow.block.velocityMultiplier + + } + + private val jumpVelocityMultiplier: Double + get() { + val blockModifier = connection.world[positionInfo.blockPosition]?.block?.jumpVelocityMultiplier ?: 1.0 + if (blockModifier == 1.0) { + return connection.world[positionInfo.velocityPosition]?.block?.jumpVelocityMultiplier ?: 1.0 + } + return blockModifier + } + private fun sendMovementPackets() { if (Minosoft.config.config.game.camera.disableMovementSending) { return @@ -192,9 +246,9 @@ class LocalPlayerEntity( lastOnGround = onGround } - private fun slipperinessToMovementSpeed(slipperiness: Double): Double { + private fun frictionToMovement(friction: Double): Double { if (onGround) { - return walkingSpeed * (0.21600002 / (slipperiness.pow(3))) + return walkingSpeed * (0.21600002 / (friction.pow(3))) } return flyingSpeed } @@ -236,9 +290,9 @@ class LocalPlayerEntity( // ToDo: Check for piston movement+ - if (!velocityMultiplier.empty) { - movement = movement * velocityMultiplier - velocityMultiplier = Vec3d.EMPTY + if (!movementMultiplier.empty) { + movement = movement * movementMultiplier + movementMultiplier = Vec3d.EMPTY velocity = Vec3d.EMPTY } @@ -271,20 +325,43 @@ class LocalPlayerEntity( } // ToDo: Check for move effect + + val velocityMultiplier = velocityMultiplier + velocity.x *= velocityMultiplier + velocity.z *= velocityMultiplier + } + + private fun applyClimbingSpeed(velocity: Vec3d): Vec3d { + if (!isClimbing) { + return velocity + } + this.fallDistance = 0.0 + val returnVelocity = Vec3d( + x = MMath.clamp(velocity.x, -CLIMBING_CLAMP_VALUE, CLIMBING_CLAMP_VALUE), + y = max(velocity.y, -CLIMBING_CLAMP_VALUE), + z = MMath.clamp(velocity.z, -CLIMBING_CLAMP_VALUE, CLIMBING_CLAMP_VALUE) + ) + if (returnVelocity.y < 0.0 && connection.world[positionInfo.blockPosition]?.block?.resourceLocation != DefaultBlocks.SCAFFOLDING && isSneaking) { + returnVelocity.y = 0.0 + } + return returnVelocity } - private fun move(sidewaysSpeed: Float, forwardSpeed: Float, slipperiness: Double): Vec3d { - velocity = velocity + calculateVelocity(sidewaysSpeed, forwardSpeed, slipperinessToMovementSpeed(slipperiness), rotation.headYaw) + private fun move(sidewaysSpeed: Float, forwardSpeed: Float, friction: Double): Vec3d { + velocity = velocity + calculateVelocity(sidewaysSpeed, forwardSpeed, frictionToMovement(friction), rotation.headYaw) + + velocity = applyClimbingSpeed(velocity) move(velocity) return adjustVelocityForClimbing(velocity) } private fun adjustVelocityForClimbing(velocity: Vec3d): Vec3d { - // ToDo: Check climbing or powder snow + if ((this.horizontalCollision || isJumping) && (isClimbing || connection.world[positionInfo.blockPosition]?.block == DefaultBlocks.POWDER_SNOW && equipment[InventorySlots.EquipmentSlots.FEET]?.item?.resourceLocation == DefaultItems.LEATHER_BOOTS)) { + return Vec3d(velocity.x, 0.2, velocity.z) + } return velocity - } private fun travel(sidewaysSpeed: Float, forwardSpeed: Float) { @@ -319,14 +396,12 @@ class LocalPlayerEntity( isFlyingWithElytra -> { } else -> { - val velocityPosition = Vec3i(position.x, position.y + defaultAABB.min.y - 0.5000001f, position.z) - - val slipperiness = /* ToDo: connection.world.connection.world[velocityPosition]?.block?.slipperiness ?: */0.6 + val friction = connection.world.connection.world[positionInfo.velocityPosition]?.block?.friction ?: 0.6 speedMultiplier = 0.91 if (onGround) { - speedMultiplier *= slipperiness + speedMultiplier *= friction } - val velocity = move(sidewaysSpeed, forwardSpeed, slipperiness) + val velocity = move(sidewaysSpeed, forwardSpeed, friction) activeStatusEffects[connection.registries.statusEffectRegistry[DefaultStatusEffects.LEVITATION]]?.let { @@ -434,7 +509,7 @@ class LocalPlayerEntity( private fun jump() { - var velocity = 0.42 // ToDo: Check for special blocks (e.g. soul sand, slime blocks, honey blocks, etc) + var velocity = 0.42 * jumpVelocityMultiplier activeStatusEffects[connection.registries.statusEffectRegistry[DefaultStatusEffects.JUMP_BOOST]]?.let { velocity += 0.1 * (it.amplifier + 1.0) @@ -443,7 +518,7 @@ class LocalPlayerEntity( if (isSprinting) { val yawRad = rotation.headYaw.rad - this.velocity = this.velocity + Vec3(-(sin(yawRad) * 0.2f), 0.0f, cos(yawRad) * 0.2f) + this.velocity = this.velocity + Vec3(-(yawRad.sin * 0.2f), 0.0f, yawRad.cos * 0.2f) } dirtyVelocity = true } @@ -465,4 +540,10 @@ class LocalPlayerEntity( lastFovMultiplier = currentFovMultiplier currentFovMultiplier = MMath.clamp(1.0 + walkingSpeed, 1.0, 1.5) } + + companion object { + private val CLIMBABLE_TAG = "minecraft:climbable".asResourceLocation() + private val SOUL_SPEED_BLOCKS = "minecraft:soul_speed_blocks".asResourceLocation() + private const val CLIMBING_CLAMP_VALUE = 0.15f.toDouble() + } } diff --git a/src/main/java/de/bixilon/minosoft/data/tags/DefaultBlockTags.kt b/src/main/java/de/bixilon/minosoft/data/tags/DefaultBlockTags.kt new file mode 100644 index 000000000..c4820d496 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/tags/DefaultBlockTags.kt @@ -0,0 +1,20 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.tags + +import de.bixilon.minosoft.data.mappings.blocks.DefaultBlocks + +object DefaultBlockTags { + val CLIMBABLE = setOf(DefaultBlocks.LADDER, DefaultBlocks.VINE, DefaultBlocks.SCAFFOLDING, DefaultBlocks.WEEPING_VINES, DefaultBlocks.WEEPING_VINES_PLANT, DefaultBlocks.TWISTING_VINES, DefaultBlocks.WEEPING_VINES_PLANT, DefaultBlocks.CAVE_VINES, DefaultBlocks.CAVE_VINES_PLANT) +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/EntityPositionInfo.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/EntityPositionInfo.kt index bf0b946f0..3b354270a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/EntityPositionInfo.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/EntityPositionInfo.kt @@ -29,6 +29,8 @@ class EntityPositionInfo( ) { lateinit var blockPosition: Vec3i private set + lateinit var velocityPosition: Vec3i + private set lateinit var chunkPosition: Vec2i private set lateinit var inChunkSectionPosition: Vec3i @@ -44,6 +46,7 @@ class EntityPositionInfo( fun update() { blockPosition = entity.position.blockPosition + velocityPosition = Vec3i(entity.position.x, entity.position.y + -0.5000001f, entity.position.z) chunkPosition = blockPosition.chunkPosition inChunkSectionPosition = blockPosition.inChunkSectionPosition sectionHeight = blockPosition.sectionHeight diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TagsS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TagsS2CP.kt index 3e3bb1c7b..b76344aee 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TagsS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TagsS2CP.kt @@ -30,7 +30,7 @@ class TagsS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { init { val tags: MutableMap>> = mutableMapOf() if (buffer.versionId < ProtocolVersions.V_20W51A) { - tags[BLOCK_TAG_RESOURCE_LOCATION] = buffer.readTagArray { buffer.connection.registries.getBlockState(it)!! } + tags[BLOCK_TAG_RESOURCE_LOCATION] = buffer.readTagArray { buffer.connection.registries.blockRegistry[it] } tags[ITEM_TAG_RESOURCE_LOCATION] = buffer.readTagArray { buffer.connection.registries.itemRegistry[it] } tags[FLUID_TAG_RESOURCE_LOCATION] = buffer.readTagArray { buffer.connection.registries.fluidRegistry[it] } // ToDo: when was this added? Was not available in 18w01 if (buffer.versionId >= ProtocolVersions.V_18W43A) { diff --git a/src/main/resources/assets/minosoft/mapping/pixlyzer_index.json b/src/main/resources/assets/minosoft/mapping/pixlyzer_index.json index 384465d5f..0f21b42a4 100644 --- a/src/main/resources/assets/minosoft/mapping/pixlyzer_index.json +++ b/src/main/resources/assets/minosoft/mapping/pixlyzer_index.json @@ -1 +1 @@ -{"21w14a":"e6d593be546e6debba5224a0bef84c5d2a7e2aa1","21w13a":"199aa5dade84d1a1064cee0e698742819f3636e9","21w11a":"6cbf3d0fa67cb8ea7a2c81fc92d5dab1dc68c425","21w10a":"11a38a9c0bec705b7c1e6130b3323bc7f7249edc","21w08b":"b442eac3b322b691d08b0ef63daa6906ef48c537","21w08a":"4a27d17ad9b35405e3bcc4ec3810e8c511a474b7","21w07a":"b43b3cb7e675bbd51b7c51046e90aa41568526cf","21w06a":"c0d8325e3ba0a9bc605fa4eeeacbd67e363928d4","21w05b":"e6587d7f39890f731e4d7d07412db44dee33744a","21w05a":"ec60bddff1168d8bde667223c1e3b771eb91f9d5","21w03a":"d1d9f555c3715a002b73f18f1be87296fdab0e31","1.16.5":"dea4f64d54fd84ee7b5548a1c01f34d0e8ce0a32","20w51a":"bd63a1ba16b6bb44135432acf78165b7869c8722","20w49a":"792be4ffd409d1f687d75bcdd55182a8ae1a776d","20w48a":"079eb36838a35e42d6ffca91380233acc1769e41","20w46a":"776fd8bffd8a3e83e29f4b4d0ad12253d4f15525","20w45a":"e4254c7fa4e157c5c530ca7d6bdc49f0b697b422","1.16.2-pre3":"515ca78acd8bf57b6ca7ca772ad685c5d8a933a3","1.16.2-pre2":"5e9e33812c125c8ff71ae92e7245e82b02582c29","1.16.2-pre1":"97ded5cfb0545d60e573ea1f58924961dd746abb","20w30a":"f3cf0ee469888569a4f6ed0108cc7845a2de86b4","20w29a":"3804d238e3ce3c6e6eb492f70437246a64413926","20w28a":"940bde46a8293afebffc7d952e9530e4683f5c88","20w27a":"d8c9f0a7574c3a5e21fc51bc5be44a5063339841","1.16.1":"f1f1bb5744e3469fb1c2b473adc99b9500af14b9","1.16":"f7d0ef3f39000b703c0a72f0c8e50108e28f258a","1.16-rc1":"059bbaa7f788ffc3a1250f09549ee38d954e54bf","1.16-pre8":"ef46c8a733394f449d45ef09b940a8996dd7ae6a","1.16-pre7":"95950760e775c1dbdaa19ec2c7c5ac3276e2733a","1.16-pre6":"6492b244fa4becbe561c5d50e0e9c12b05c1a1da","1.16-pre5":"0348781588a0e817ba8b11ef6cd2ae31802b5721","1.16-pre4":"4af63ace1aecf28f1f1244eabbeb91eeded86428","1.16-pre3":"ca94f4866082dc2f4c6eca7fb34b29881562d53b","1.16-pre2":"f573dbd117f34a67f2a10b84ab1e68e85cc92ecb","1.16-pre1":"a3a349a4391d0a64b580889f286d773f5ee2c1d8","20w22a":"deb129f92842a49d66f255ab6e0518b5918d3cf1","20w21a":"25a4cbec04bf93564d025f920c17e25fa3a7efdf","20w20b":"da188302d9d6e5ec985aeea70f4a58c475c8c7d9","20w20a":"3eaad496406d25c64745a8fb93367b3d22512713","20w19a":"19c9d9a3d5ba9022fa506bfd39648eb16c4cf853","20w18a":"8e54369d7f46fba7d65802c88c14e108e8ab8c51","20w17a":"c16f4421c599ddc7a95b92efd2626a866fca2213","20w16a":"a6cf1a009260ba8b71aaa67744ee8a7be7e70b57","20w15a":"5fe632f9717749e617ed21eb47c10d5406badc88","20w14a":"42baa10fc946dbb17b53ac5917195d66ec017129","20w14infinite":"7741966897b6c0ab48c1bf698f3c5bca5afa06bb","20w13b":"2e7eda27813bb50f63fa56f09b7d21bdce470521","20w13a":"3c6c81f96f7f423f1003809ee5a8bb218f55675c","20w12a":"f1d32139c70a2880984cfa3cbc4e4cbb1b29c4bf","20w11a":"90356446c48da3506e7a070bd119cf6076b400fa","20w10a":"e5cf2d235129b4d94611769a3cc02b90f11684a9","20w09a":"caaf4f25d9138a564d14cfb8bc871962d69c939d","20w08a":"38a8aa2a17cba832cb1bfa056a6ba2ca2d7181d4","20w07a":"a2d18666745a8dc106ee9754c804942789b051cf","20w06a":"9fa356bc803d424a022148f53ee97c3534357ca6","1.16.5-rc1":"b15125be1d323b297ba6c993374312e9e8e3c10e","1.16.4":"2f8ecd056f6b7e659617b0be89a4fd2a9403576e","1.16.4-rc1":"dd3a5b3d07dd299097e953c27842db61dc1ffebb","1.16.4-pre2":"30b336ae7556379f5fe817cfebcae9fd694f065d","1.16.4-pre1":"d1271ce413a2bff7ae4af91c56c04a0482de1627","1.16.3":"bee3f70fa6162f22d56a62ed35238800b0752c8d","1.16.3-rc1":"feb2344111ed43b68cb49cf9e0ae96e15c74b615","1.16.2":"7f5cbdbe9b15b0650e9a05d7315e98ae8ae2abc2","1.16.2-rc2":"eb23d0122e00cf92a047d5a2b256a72016f26171","1.16.2-rc1":"bca807e4e48517da6a3afab16bfd213cae0200df","1.15.2":"9a1c15a2e7026a7eabb6f6758b9c22354d695fcf","1.15.2-pre2":"04cbc2299458cf56986355d93c33438526ff4fa7","1.15.2-pre1":"0d01733473d0108c054dfc1be66f272b6148b9f7","1.15.1":"6b8f32f451fa6dda72d84bf72b79ae2d8a42f1bd","1.15.1-pre1":"324279aed5fdde77b8fdf6ac867c5a788d390666","1.15":"7d66a88d9b1f3b41cfa9aee5c53ac1345e11afa5","1.15-pre7":"191373506adf6ecbb3a9dfdf0366fe335b47638e","1.15-pre6":"54722cf0e6e22bd72e468c1a7aaf2fa3b00c631b","1.15-pre5":"63a68682c4c39f6c8ff64a6b40761565d5de06f2","1.15-pre4":"8dd01a2b9ccf0635af9b43e7bc886fe66606e45d","1.15-pre3":"fc6326b6063174ac4a6b08bb6c708f201c4b39af","1.15-pre2":"5ef9c24c06ba9f102525d9b1f127bab22909d66c","1.15-pre1":"813ba86f56b1e8e9e733d69fc068d9de0ded621d","19w46b":"e0f6146f3b49e933066fa952cd845aac593aa6cd","19w46a":"99a8e36e643311b3dcb7d9be610d592da4d644aa","19w45b":"a988ba600404b51ae2e39f483264cc00dd78a71a","19w45a":"a111f84e9ff295e294befd221c8df88c1f6e9866","19w44a":"68306c8c062ad03b463fd28e5fe9a86701482935","19w42a":"ee331c7b22c02ce46e36196ed65bb02b297bcdec","19w41a":"408d1391dbbe5bd2ee35bc4d740f1650c7f05dcd","19w40a":"9684079f941dd82f1f82123fcdd4672cc454d7d3","19w39a":"b811604cf6e43519016292bc30f902d372961576","19w38b":"42f756a93f0522a9c893f036c3de078461cf5701","19w38a":"4693891cff2c8d76661a62cf71cd68841643d7b0","19w37a":"8dbbb3548ce39678bf7059961ce2010563a9b680","19w36a":"28d53d8d7da4ca8d5a7b852362283e5e3bc284c1","19w35a":"7719d28931592ab90c70f89a4e3d2d261b092349","19w34a":"a394a85300f6257346583ec8750f952a8dea89a4","1.14.4":"610b3233f9975fc213e044640095e3002f3039ac","1.14.4-pre7":"210e5284ad1cb1242e90db9648c099fe96276a89","1.14.4-pre6":"0c57ddb54cd544a59b8587b23d027a0752d07916","1.14.4-pre5":"bae283fa6982fcda53e220e2ae53c24f18875043","1.14.4-pre4":"fabc63854536728294d09e56a1beece72fb16771","1.14.4-pre3":"2195ae6a3ad8d6279ca4648c481d480c46c1e9fc","1.14.4-pre2":"d20214fb2ac3e018e1a074063bb0c7c684bdf162","1.14.4-pre1":"81237a0354f97c397331fca74aef8631ce0cca33","1.14.3":"c260bc3009ba42bac5133c175c08d9417e620fb7","1.14.3-pre4":"a79220b67d7758f639945d80d001f8b0135ce65d","1.14.3-pre3":"62ef8dd85c438b04db73e953523c4492ebafbfa1","1.14.3-pre2":"8bedd60b99657e0e5cf83b8e1594788c6055b877","1.14.3-pre1":"bcde219f5c3a8f2d616e6577e07793ffb296bd6b","1.14.2":"58266ec4294c63276c7f628c2f41fef6a10c95cb","1.14.2 Pre-Release 4":"ba4891c3126023af3807d7624a7f069e3247eb0c","1.14.2 Pre-Release 3":"13dd1a14b589abb6eeb1a58b1c208109dae1600f","1.14.2 Pre-Release 2":"54f04ec460dfbdb0b543b6afab4a29c76702261f","1.14.2 Pre-Release 1":"71354888138db50749a2fdb4512c3055daf3f858","1.14.1":"6eb3209ee41c3a3fab6a3834e150972b39992a5f","1.14.1 Pre-Release 2":"f621a5bc8d428b5b9140c9a6ab54ca6ca6772389","1.14.1 Pre-Release 1":"bba13cecb85753c66c38e477f6fd47b43e9b5194","1.14":"6ced213dab0f59fc8629d8c5e658373eb9cf5d0c","1.14 Pre-Release 5":"5317a95d449a6258993e652f2602436cee4d3796","1.14 Pre-Release 4":"8dfcb0d985e4fd7162655c8e0eb7731032ac2bf3","1.14 Pre-Release 3":"fea442b33b2f4815272824905f65611477464c5e","1.14 Pre-Release 2":"1953e5b3944c1a8cea3618abbca0c7879398a0cd","1.14 Pre-Release 1":"488264eba3556b8549c0241fc19bc986c864f34f","19w14b":"cce33de8de23a089d3d7d07426ba14cf843cca05","19w14a":"e16b97684c811097a074012f7f22f154adb78f46","3D Shareware v1.34":"9871622b5cf87e2b658a2a4bd0ae403ab663fddc","19w13b":"de7b1e6ebda633d813fd6c7e0a54f409b95fd0c9","19w13a":"c9c8d67c181bfb9eb26a78f11237780c3eaf410e","19w12b":"ca8b33890db962d9b1806ab427aeff7e30e32305","19w12a":"7df41ec7abb67a86a9fbf3fb1aceb20d3a4fae39","19w11b":"054bc37ae4daa7d1361aaf77e321f2044678cc1b","19w11a":"cafef567ac2bcdbf2f2df48cd7e624f224ab10dc","21w15a":"d953c42de1ae88da729d51186dfd248e9ec45664","17w46a":"153dff89ed90f91758f847907cd4d48d3b553a3f","21w16a":"28bf5839c5f9dffc857cb16fdf08bf3b1e205544","21w17a":"93ac103f10182fe26f8ce6864914de67bf21e338","21w18a":"c4f1382c7775bf5de38a2e1cc286113d7fba2680","21w19a":"6fcb1a5ffb23787b74dd59a219a37872a8f52c47","21w20a":"1007ff40be00061c86473fa1bf63f999dd723469","1.17-pre1":"c3e8af56b10eed50680c1be327de40791f4f8fff","1.17-pre2":"0f8a9828c94c98ad578bd899ed95a2c600d163bd","1.17-pre3":"289f6a10b549997abe7cb87552ea57d776902d82","1.17-pre4":"5d6e1452a026c35ded35b0ddfeb9e39cbe2432e5","1.17-pre5":"8ad72c057f3ac37f922358b1498826c02ce5c541","1.17-rc1":"0102babdeda9f81391ce33e3fda5b195d899f6d7"} +{"21w14a":"e6d593be546e6debba5224a0bef84c5d2a7e2aa1","21w13a":"199aa5dade84d1a1064cee0e698742819f3636e9","21w11a":"6cbf3d0fa67cb8ea7a2c81fc92d5dab1dc68c425","21w10a":"11a38a9c0bec705b7c1e6130b3323bc7f7249edc","21w08b":"b442eac3b322b691d08b0ef63daa6906ef48c537","21w08a":"4a27d17ad9b35405e3bcc4ec3810e8c511a474b7","21w07a":"b43b3cb7e675bbd51b7c51046e90aa41568526cf","21w06a":"c0d8325e3ba0a9bc605fa4eeeacbd67e363928d4","21w05b":"e6587d7f39890f731e4d7d07412db44dee33744a","21w05a":"ec60bddff1168d8bde667223c1e3b771eb91f9d5","21w03a":"d1d9f555c3715a002b73f18f1be87296fdab0e31","1.16.5":"bded6003b9c290732f8f37247dbb2d8ac07abcbe","20w51a":"bd63a1ba16b6bb44135432acf78165b7869c8722","20w49a":"792be4ffd409d1f687d75bcdd55182a8ae1a776d","20w48a":"079eb36838a35e42d6ffca91380233acc1769e41","20w46a":"776fd8bffd8a3e83e29f4b4d0ad12253d4f15525","20w45a":"e4254c7fa4e157c5c530ca7d6bdc49f0b697b422","1.16.2-pre3":"515ca78acd8bf57b6ca7ca772ad685c5d8a933a3","1.16.2-pre2":"5e9e33812c125c8ff71ae92e7245e82b02582c29","1.16.2-pre1":"97ded5cfb0545d60e573ea1f58924961dd746abb","20w30a":"f3cf0ee469888569a4f6ed0108cc7845a2de86b4","20w29a":"3804d238e3ce3c6e6eb492f70437246a64413926","20w28a":"940bde46a8293afebffc7d952e9530e4683f5c88","20w27a":"d8c9f0a7574c3a5e21fc51bc5be44a5063339841","1.16.1":"f1f1bb5744e3469fb1c2b473adc99b9500af14b9","1.16":"f7d0ef3f39000b703c0a72f0c8e50108e28f258a","1.16-rc1":"059bbaa7f788ffc3a1250f09549ee38d954e54bf","1.16-pre8":"ef46c8a733394f449d45ef09b940a8996dd7ae6a","1.16-pre7":"95950760e775c1dbdaa19ec2c7c5ac3276e2733a","1.16-pre6":"6492b244fa4becbe561c5d50e0e9c12b05c1a1da","1.16-pre5":"0348781588a0e817ba8b11ef6cd2ae31802b5721","1.16-pre4":"4af63ace1aecf28f1f1244eabbeb91eeded86428","1.16-pre3":"ca94f4866082dc2f4c6eca7fb34b29881562d53b","1.16-pre2":"f573dbd117f34a67f2a10b84ab1e68e85cc92ecb","1.16-pre1":"a3a349a4391d0a64b580889f286d773f5ee2c1d8","20w22a":"deb129f92842a49d66f255ab6e0518b5918d3cf1","20w21a":"25a4cbec04bf93564d025f920c17e25fa3a7efdf","20w20b":"da188302d9d6e5ec985aeea70f4a58c475c8c7d9","20w20a":"3eaad496406d25c64745a8fb93367b3d22512713","20w19a":"19c9d9a3d5ba9022fa506bfd39648eb16c4cf853","20w18a":"8e54369d7f46fba7d65802c88c14e108e8ab8c51","20w17a":"c16f4421c599ddc7a95b92efd2626a866fca2213","20w16a":"a6cf1a009260ba8b71aaa67744ee8a7be7e70b57","20w15a":"5fe632f9717749e617ed21eb47c10d5406badc88","20w14a":"42baa10fc946dbb17b53ac5917195d66ec017129","20w14infinite":"7741966897b6c0ab48c1bf698f3c5bca5afa06bb","20w13b":"2e7eda27813bb50f63fa56f09b7d21bdce470521","20w13a":"3c6c81f96f7f423f1003809ee5a8bb218f55675c","20w12a":"f1d32139c70a2880984cfa3cbc4e4cbb1b29c4bf","20w11a":"90356446c48da3506e7a070bd119cf6076b400fa","20w10a":"e5cf2d235129b4d94611769a3cc02b90f11684a9","20w09a":"caaf4f25d9138a564d14cfb8bc871962d69c939d","20w08a":"38a8aa2a17cba832cb1bfa056a6ba2ca2d7181d4","20w07a":"a2d18666745a8dc106ee9754c804942789b051cf","20w06a":"9fa356bc803d424a022148f53ee97c3534357ca6","1.16.5-rc1":"b15125be1d323b297ba6c993374312e9e8e3c10e","1.16.4":"2f8ecd056f6b7e659617b0be89a4fd2a9403576e","1.16.4-rc1":"dd3a5b3d07dd299097e953c27842db61dc1ffebb","1.16.4-pre2":"30b336ae7556379f5fe817cfebcae9fd694f065d","1.16.4-pre1":"d1271ce413a2bff7ae4af91c56c04a0482de1627","1.16.3":"bee3f70fa6162f22d56a62ed35238800b0752c8d","1.16.3-rc1":"feb2344111ed43b68cb49cf9e0ae96e15c74b615","1.16.2":"7f5cbdbe9b15b0650e9a05d7315e98ae8ae2abc2","1.16.2-rc2":"eb23d0122e00cf92a047d5a2b256a72016f26171","1.16.2-rc1":"bca807e4e48517da6a3afab16bfd213cae0200df","1.15.2":"e965f537ae157dcf2e3b3706be9767c21ebc5d70","1.15.2-pre2":"04cbc2299458cf56986355d93c33438526ff4fa7","1.15.2-pre1":"0d01733473d0108c054dfc1be66f272b6148b9f7","1.15.1":"6b8f32f451fa6dda72d84bf72b79ae2d8a42f1bd","1.15.1-pre1":"324279aed5fdde77b8fdf6ac867c5a788d390666","1.15":"7d66a88d9b1f3b41cfa9aee5c53ac1345e11afa5","1.15-pre7":"191373506adf6ecbb3a9dfdf0366fe335b47638e","1.15-pre6":"54722cf0e6e22bd72e468c1a7aaf2fa3b00c631b","1.15-pre5":"63a68682c4c39f6c8ff64a6b40761565d5de06f2","1.15-pre4":"8dd01a2b9ccf0635af9b43e7bc886fe66606e45d","1.15-pre3":"fc6326b6063174ac4a6b08bb6c708f201c4b39af","1.15-pre2":"5ef9c24c06ba9f102525d9b1f127bab22909d66c","1.15-pre1":"813ba86f56b1e8e9e733d69fc068d9de0ded621d","19w46b":"e0f6146f3b49e933066fa952cd845aac593aa6cd","19w46a":"99a8e36e643311b3dcb7d9be610d592da4d644aa","19w45b":"a988ba600404b51ae2e39f483264cc00dd78a71a","19w45a":"a111f84e9ff295e294befd221c8df88c1f6e9866","19w44a":"68306c8c062ad03b463fd28e5fe9a86701482935","19w42a":"ee331c7b22c02ce46e36196ed65bb02b297bcdec","19w41a":"408d1391dbbe5bd2ee35bc4d740f1650c7f05dcd","19w40a":"9684079f941dd82f1f82123fcdd4672cc454d7d3","19w39a":"b811604cf6e43519016292bc30f902d372961576","19w38b":"42f756a93f0522a9c893f036c3de078461cf5701","19w38a":"4693891cff2c8d76661a62cf71cd68841643d7b0","19w37a":"8dbbb3548ce39678bf7059961ce2010563a9b680","19w36a":"28d53d8d7da4ca8d5a7b852362283e5e3bc284c1","19w35a":"7719d28931592ab90c70f89a4e3d2d261b092349","19w34a":"a394a85300f6257346583ec8750f952a8dea89a4","1.14.4":"610b3233f9975fc213e044640095e3002f3039ac","1.14.4-pre7":"210e5284ad1cb1242e90db9648c099fe96276a89","1.14.4-pre6":"0c57ddb54cd544a59b8587b23d027a0752d07916","1.14.4-pre5":"bae283fa6982fcda53e220e2ae53c24f18875043","1.14.4-pre4":"fabc63854536728294d09e56a1beece72fb16771","1.14.4-pre3":"2195ae6a3ad8d6279ca4648c481d480c46c1e9fc","1.14.4-pre2":"d20214fb2ac3e018e1a074063bb0c7c684bdf162","1.14.4-pre1":"81237a0354f97c397331fca74aef8631ce0cca33","1.14.3":"c260bc3009ba42bac5133c175c08d9417e620fb7","1.14.3-pre4":"a79220b67d7758f639945d80d001f8b0135ce65d","1.14.3-pre3":"62ef8dd85c438b04db73e953523c4492ebafbfa1","1.14.3-pre2":"8bedd60b99657e0e5cf83b8e1594788c6055b877","1.14.3-pre1":"bcde219f5c3a8f2d616e6577e07793ffb296bd6b","1.14.2":"58266ec4294c63276c7f628c2f41fef6a10c95cb","1.14.2 Pre-Release 4":"ba4891c3126023af3807d7624a7f069e3247eb0c","1.14.2 Pre-Release 3":"13dd1a14b589abb6eeb1a58b1c208109dae1600f","1.14.2 Pre-Release 2":"54f04ec460dfbdb0b543b6afab4a29c76702261f","1.14.2 Pre-Release 1":"71354888138db50749a2fdb4512c3055daf3f858","1.14.1":"6eb3209ee41c3a3fab6a3834e150972b39992a5f","1.14.1 Pre-Release 2":"f621a5bc8d428b5b9140c9a6ab54ca6ca6772389","1.14.1 Pre-Release 1":"bba13cecb85753c66c38e477f6fd47b43e9b5194","1.14":"6ced213dab0f59fc8629d8c5e658373eb9cf5d0c","1.14 Pre-Release 5":"5317a95d449a6258993e652f2602436cee4d3796","1.14 Pre-Release 4":"8dfcb0d985e4fd7162655c8e0eb7731032ac2bf3","1.14 Pre-Release 3":"fea442b33b2f4815272824905f65611477464c5e","1.14 Pre-Release 2":"1953e5b3944c1a8cea3618abbca0c7879398a0cd","1.14 Pre-Release 1":"488264eba3556b8549c0241fc19bc986c864f34f","19w14b":"cce33de8de23a089d3d7d07426ba14cf843cca05","19w14a":"e16b97684c811097a074012f7f22f154adb78f46","3D Shareware v1.34":"9871622b5cf87e2b658a2a4bd0ae403ab663fddc","19w13b":"de7b1e6ebda633d813fd6c7e0a54f409b95fd0c9","19w13a":"c9c8d67c181bfb9eb26a78f11237780c3eaf410e","19w12b":"ca8b33890db962d9b1806ab427aeff7e30e32305","19w12a":"7df41ec7abb67a86a9fbf3fb1aceb20d3a4fae39","19w11b":"054bc37ae4daa7d1361aaf77e321f2044678cc1b","19w11a":"cafef567ac2bcdbf2f2df48cd7e624f224ab10dc","21w15a":"d953c42de1ae88da729d51186dfd248e9ec45664","17w46a":"153dff89ed90f91758f847907cd4d48d3b553a3f","21w16a":"28bf5839c5f9dffc857cb16fdf08bf3b1e205544","21w17a":"93ac103f10182fe26f8ce6864914de67bf21e338","21w18a":"c4f1382c7775bf5de38a2e1cc286113d7fba2680","21w19a":"6fcb1a5ffb23787b74dd59a219a37872a8f52c47","21w20a":"1007ff40be00061c86473fa1bf63f999dd723469","1.17-pre1":"c3e8af56b10eed50680c1be327de40791f4f8fff","1.17-pre2":"0f8a9828c94c98ad578bd899ed95a2c600d163bd","1.17-pre3":"289f6a10b549997abe7cb87552ea57d776902d82","1.17-pre4":"5d6e1452a026c35ded35b0ddfeb9e39cbe2432e5","1.17-pre5":"8ad72c057f3ac37f922358b1498826c02ce5c541","1.17-rc1":"3729b77b4f6b2542e448ae1f337a16e45d4e2965"}