Compare commits

...
61 Commits
Author SHA1 Message Date
Nassim Jahnke b4870a49e3 1.19.3-rc3 protocol changes (but not actually supporting the snapshot)
Definitely still 4.5.0
2022-12-06 12:20:48 +01:00
Nassim Jahnke f7a48d028b 4.5.0 Release 2022-12-05 20:29:24 +01:00
Nassim Jahnke 382ef3953a Merge remote-tracking branch 'origin/master' into dev 2022-12-05 19:55:29 +01:00
Nassim Jahnke 9be32ca18b Make slightly more sense of CompactArrayUtil
... and make it less vanilla copy pasty

The magic array is a reciprocal table of 3-tuples, although 1 and 2 are always the same but for the ones including right shifts
2022-12-02 14:34:32 +01:00
Nassim Jahnke e0cd121a58 1.19.3-rc1 2022-12-01 16:41:19 +01:00
Nassim Jahnke 96631cee0c 1.19.3-pre3 2022-11-29 17:26:51 +01:00
Nassim Jahnke 8c0487fc05 1.19.3-pre2 2022-11-23 18:02:33 +01:00
Nassim Jahnke aec60f8cef 1.19.3-pre1 2022-11-22 17:11:11 +01:00
Nassim Jahnke a3080800b0 More minor refactoring 2022-11-22 17:04:06 +01:00
Kevin Ludwig 85f9414b95 Fix recipe book not being empty when using a multi-server configuration, with servers which are pre-1.12. (#3156) 2022-11-20 12:31:01 +01:00
Nassim Jahnke e7f07d1fdb Update mappings file 2022-11-16 16:39:16 +01:00
Nassim Jahnke 83efd0a267 22w46a (maybe) 2022-11-16 16:03:18 +01:00
Nassim Jahnke 5c05c61fbf 22w45a 2022-11-09 16:09:23 +01:00
Nassim Jahnke fad350c811 Small refactoring 2022-11-03 18:30:59 +01:00
Nassim Jahnke d3d8d1cab3 22w44a (maybe) 2022-11-02 15:49:40 +01:00
RK_01 a3437ca6ba Fix 1.19 -> 1.18.2 player info display name translation: Empty text edge case (#3146) 2022-10-27 10:36:46 +02:00
Kevin Ludwig 17bd048d10 Fix missing zombie_pigman (1.15.2) => zombified_piglin (1.16) translation in mob_spawner entity (#3147) 2022-10-27 10:32:31 +02:00
F 955b76f599 Fix use-new-effect-indicator option for 1.13+ clients (#3127) 2022-10-27 10:30:03 +02:00
Nassim Jahnke e3dc9e5b66 22w43a 2022-10-26 18:09:42 +02:00
Nassim Jahnke 6095128007 Remove javassist dependency, reduces file size by ~1mb 2022-10-25 17:31:13 +02:00
Nassim Jahnke 0355447d53 Remove more of the old compat layer 2022-10-25 15:55:42 +02:00
Nassim Jahnke 58fd0bdc9e Remove 1.8 PS compat hack 2022-10-25 10:36:09 +02:00
Nassim Jahnke cdc35aa5dc Merge remote-tracking branch 'origin/master' into dev 2022-10-25 10:36:02 +02:00
RK_01 10efba31cf Update 1.9 -> 1.8 ATTACH_ENTITY data reading (#3144) 2022-10-25 10:35:45 +02:00
Nassim Jahnke ef204c2ab2 Fix argument type handling 2022-10-23 18:27:44 +02:00
Nassim Jahnke 555e1a7722 Fix long metatype reading, some internal type reading changes 2022-10-22 10:19:27 +02:00
Nassim Jahnke 7400784e4f 22w42 (except for command completions) 2022-10-20 19:34:04 +02:00
Nassim Jahnke 2154f0a2cf Almost works 2022-10-19 23:18:39 +02:00
Nassim Jahnke 14b11bdd10 Start working on 22w42a 2022-10-19 21:58:08 +02:00
Nassim Jahnke c5738a2203 Add Via channel handlers instead of wrapping Vanilla handlers on Bukkit (#3132) 2022-10-19 21:29:24 +02:00
MrMicky 43ad855499 Rewrite signs and books components on 1.16 (#2712) 2022-10-16 21:22:48 +02:00
Kichura 2533619339 Update actions, Gradle 7.5.1 (#3139) 2022-10-15 19:20:20 +02:00
Nassim Jahnke b133dad642 Fix random long gen for seeded sounds 2022-10-08 21:50:09 +02:00
RK_01 464a85c52b Replaced hardcoded chunk section out of bounds check (#3136) 2022-10-07 23:07:50 +02:00
Gero 7698ee7683 Some minor performance improvements and cleanups (#3134) 2022-10-04 13:25:55 +02:00
BuzzTheGamer23 c7eb2e4b97 Change Maven version from deprecated LATEST (#2913) 2022-10-01 11:23:02 +02:00
creeper123123321 e71c88f860 Add sanity checks for some block nbt (#3129) 2022-10-01 10:14:41 +02:00
RK_01 c96b2bd859 Fix 1.19 -> 1.18.2 title translation: empty text edge case (#3115) 2022-09-02 16:00:45 +02:00
Nassim Jahnke ce4f21b7d8 Fix Paper packet limiter check on new versions 2022-08-25 13:03:40 +02:00
Nassim Jahnke 2a55025e99 Version bump 2022-08-21 14:58:00 +02:00
Nassim Jahnke c86da22f4e Fix config header duplication on reload
Fixes #3100
2022-08-21 14:57:32 +02:00
Nassim Jahnke adb10f5fe7 4.4.2 Release 2022-08-21 09:42:05 +02:00
Pablo Herrera a268d3d298 Fix userconnection availability on join event (PoC) (#2849) 2022-08-19 11:59:48 +02:00
creeper123123321 b60771e96e Fix block connection on non-full chunks (#3068) 2022-08-15 11:58:57 +02:00
Nassim Jahnke 435a986068 Properly enclose limiting of nbt reading
Fixes #3093
2022-08-14 22:24:31 +02:00
Nassim Jahnke 6c58f0980c Add utility method for initializing entity types 2022-08-13 15:36:57 +02:00
Nassim Jahnke e3dde71821 And the default return value 2022-08-11 19:09:33 +02:00
Nassim Jahnke 47b15e5fef Forgot a lock 2022-08-11 19:08:33 +02:00
Nassim Jahnke ed196bdf99 Clean up proxy protocol detector service 2022-08-11 19:04:22 +02:00
Nassim Jahnke 9247e87bbf Update InformativeException message 2022-08-08 21:01:27 +02:00
Nassim Jahnke 08727f959f Update 1.19.1 protocol version string 2022-08-05 16:05:31 +02:00
Nassim Jahnke 78342da180 And make the 1.19 chat registry nbt public 2022-08-04 16:29:35 +02:00
Nassim Jahnke 2b735172bd Make 1.19 chat decoration method public 2022-08-04 16:23:21 +02:00
_tomcraft b032a868bb add flamecord warking message (#3079) 2022-08-04 16:19:08 +02:00
Nassim Jahnke c4fa3b0ff8 Fix worldDataTrackerHandlerByKey world index 2022-08-04 15:51:51 +02:00
Nassim Jahnke 0c3a1803e1 Send block entity data after block break cancellation
Fixes #3077
2022-08-03 19:45:00 +02:00
Gero 04f9971dee Alter velocity modern forwarding version (#3078) 2022-08-03 16:17:52 +02:00
Elioby 9c3395b76b Use default painting mapping of 0 instead of -1 (#3073) 2022-08-01 21:07:40 +02:00
Nassim Jahnke f9bbb5b1a3 Fixup config comment saving
Fixes #3052, #2536
2022-08-01 12:28:44 +02:00
Nassim Jahnke 087bbdce1f Fix 1.19+ sign opening on ~1.12 servers
Fixes #3027
2022-07-30 18:10:45 +02:00
Nassim Jahnke 494bf62535 Bump version to 4.4.2-SNAPSHOT 2022-07-30 18:05:04 +02:00
129 changed files with 30727 additions and 1575 deletions

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ body:
attributes:
value: |
**Before reporting a bug, please see if using master/dev builds from https://ci.viaversion.com/ fixes your issue.**
Whenever you see fit, you can upload images or video to any of the textfields.
Whenever you see fit, you can upload images or videos to any of the text fields.
- type: input
attributes:
+4 -4
View File
@@ -10,19 +10,19 @@ jobs:
# Only run on PRs if the source branch is on a different repo. We do not need to run everything twice.
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- name: Set up JDK 11
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 11
- name: Build with Gradle
run: ./gradlew build
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: artifact
path: build/libs
+1 -1
View File
@@ -51,7 +51,7 @@ You can find official dev builds here:
<dependency>
<groupId>com.viaversion</groupId>
<artifactId>viaversion-api</artifactId>
<version>LATEST</version>
<version>[4.0.0,5.0.0)</version>
<scope>provided</scope>
</dependency>
```
@@ -85,6 +85,7 @@ public class ProtocolVersion {
public static final ProtocolVersion v1_18_2 = register(758, "1.18.2");
public static final ProtocolVersion v1_19 = register(759, "1.19");
public static final ProtocolVersion v1_19_1 = register(760, "1.19.1");
public static final ProtocolVersion v1_19_3 = register(761, "1.19.3");
public static final ProtocolVersion unknown = register(-1, "UNKNOWN");
public static ProtocolVersion register(int version, String name) {
@@ -25,6 +25,7 @@ package com.viaversion.viaversion.api;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.platform.ViaServerProxyPlatform;
public final class Via {
private static ViaManager manager;
@@ -63,6 +64,11 @@ public final class Via {
return manager().getPlatform();
}
public static ViaServerProxyPlatform<?> proxyPlatform() {
Preconditions.checkArgument(manager().getPlatform() instanceof ViaServerProxyPlatform, "Platform is not proxying Minecraft servers!");
return (ViaServerProxyPlatform<?>) manager().getPlatform();
}
/**
* Register the ViaManager associated with the platform.
*
@@ -64,7 +64,7 @@ public interface ViaAPI<T> {
* @return API version incremented with meaningful API changes
*/
default int apiVersion() {
return 14;
return 15;
}
/**
@@ -42,4 +42,6 @@ public interface BlockEntity {
int typeId();
@Nullable CompoundTag tag();
BlockEntity withTypeId(int typeId);
}
@@ -56,4 +56,9 @@ public final class BlockEntityImpl implements BlockEntity {
public CompoundTag tag() {
return tag;
}
@Override
public BlockEntity withTypeId(int typeId) {
return new BlockEntityImpl(packedXZ, y, typeId, tag);
}
}
@@ -40,6 +40,18 @@ public interface ChunkSection {
return y << 8 | z << 4 | x;
}
static int xFromIndex(int idx) {
return idx & 0xF;
}
static int yFromIndex(int idx) {
return idx >> 8 & 0xF;
}
static int zFromIndex(int idx) {
return idx >> 4 & 0xF;
}
@Deprecated/*(forRemoval = true)*/
default int getFlatBlock(int idx) {
return palette(PaletteType.BLOCKS).idAt(idx);
@@ -269,15 +269,6 @@ public enum Entity1_19Types implements EntityType {
}
public static void initialize(final Protocol<?, ?, ?, ?> protocol) {
for (final Entity1_19Types type : values()) {
if (type.isAbstractType()) {
continue;
}
final int id = protocol.getMappingData().getEntityMappings().mappedId(type.identifier());
Preconditions.checkArgument(id != -1, "Entity type %s has no id", type.identifier());
type.id = id;
}
EntityTypeUtil.fill(values(), TYPES);
EntityTypeUtil.initialize(values(), TYPES, protocol, (type, id) -> type.id = id);
}
}
@@ -0,0 +1,275 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.entities;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.util.EntityTypeUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale;
public enum Entity1_19_3Types implements EntityType {
ENTITY(null, null),
AREA_EFFECT_CLOUD(ENTITY),
END_CRYSTAL(ENTITY),
EVOKER_FANGS(ENTITY),
EXPERIENCE_ORB(ENTITY),
EYE_OF_ENDER(ENTITY),
FALLING_BLOCK(ENTITY),
FIREWORK_ROCKET(ENTITY),
ITEM(ENTITY),
LLAMA_SPIT(ENTITY),
TNT(ENTITY),
SHULKER_BULLET(ENTITY),
FISHING_BOBBER(ENTITY),
LIVINGENTITY(ENTITY, null),
ARMOR_STAND(LIVINGENTITY),
MARKER(ENTITY),
PLAYER(LIVINGENTITY),
ABSTRACT_INSENTIENT(LIVINGENTITY, null),
ENDER_DRAGON(ABSTRACT_INSENTIENT),
BEE(ABSTRACT_INSENTIENT),
ABSTRACT_CREATURE(ABSTRACT_INSENTIENT, null),
ABSTRACT_AGEABLE(ABSTRACT_CREATURE, null),
VILLAGER(ABSTRACT_AGEABLE),
WANDERING_TRADER(ABSTRACT_AGEABLE),
// Animals
ABSTRACT_ANIMAL(ABSTRACT_AGEABLE, null),
AXOLOTL(ABSTRACT_ANIMAL),
DOLPHIN(ABSTRACT_INSENTIENT),
CHICKEN(ABSTRACT_ANIMAL),
COW(ABSTRACT_ANIMAL),
MOOSHROOM(COW),
PANDA(ABSTRACT_INSENTIENT),
PIG(ABSTRACT_ANIMAL),
POLAR_BEAR(ABSTRACT_ANIMAL),
RABBIT(ABSTRACT_ANIMAL),
SHEEP(ABSTRACT_ANIMAL),
TURTLE(ABSTRACT_ANIMAL),
FOX(ABSTRACT_ANIMAL),
FROG(ABSTRACT_ANIMAL),
GOAT(ABSTRACT_ANIMAL),
ABSTRACT_TAMEABLE_ANIMAL(ABSTRACT_ANIMAL, null),
CAT(ABSTRACT_TAMEABLE_ANIMAL),
OCELOT(ABSTRACT_TAMEABLE_ANIMAL),
WOLF(ABSTRACT_TAMEABLE_ANIMAL),
ABSTRACT_PARROT(ABSTRACT_TAMEABLE_ANIMAL, null),
PARROT(ABSTRACT_PARROT),
// Horses
ABSTRACT_HORSE(ABSTRACT_ANIMAL, null),
CHESTED_HORSE(ABSTRACT_HORSE, null),
DONKEY(CHESTED_HORSE),
MULE(CHESTED_HORSE),
LLAMA(CHESTED_HORSE),
TRADER_LLAMA(CHESTED_HORSE),
HORSE(ABSTRACT_HORSE),
SKELETON_HORSE(ABSTRACT_HORSE),
ZOMBIE_HORSE(ABSTRACT_HORSE),
CAMEL(ABSTRACT_HORSE),
// Golem
ABSTRACT_GOLEM(ABSTRACT_CREATURE, null),
SNOW_GOLEM(ABSTRACT_GOLEM),
IRON_GOLEM(ABSTRACT_GOLEM),
SHULKER(ABSTRACT_GOLEM),
// Fish
ABSTRACT_FISHES(ABSTRACT_CREATURE, null),
COD(ABSTRACT_FISHES),
PUFFERFISH(ABSTRACT_FISHES),
SALMON(ABSTRACT_FISHES),
TROPICAL_FISH(ABSTRACT_FISHES),
// Monsters
ABSTRACT_MONSTER(ABSTRACT_CREATURE, null),
BLAZE(ABSTRACT_MONSTER),
CREEPER(ABSTRACT_MONSTER),
ENDERMITE(ABSTRACT_MONSTER),
ENDERMAN(ABSTRACT_MONSTER),
GIANT(ABSTRACT_MONSTER),
SILVERFISH(ABSTRACT_MONSTER),
VEX(ABSTRACT_MONSTER),
WITCH(ABSTRACT_MONSTER),
WITHER(ABSTRACT_MONSTER),
RAVAGER(ABSTRACT_MONSTER),
ABSTRACT_PIGLIN(ABSTRACT_MONSTER, null),
PIGLIN(ABSTRACT_PIGLIN),
PIGLIN_BRUTE(ABSTRACT_PIGLIN),
HOGLIN(ABSTRACT_ANIMAL),
STRIDER(ABSTRACT_ANIMAL),
TADPOLE(ABSTRACT_FISHES),
ZOGLIN(ABSTRACT_MONSTER),
WARDEN(ABSTRACT_MONSTER),
// Illagers
ABSTRACT_ILLAGER_BASE(ABSTRACT_MONSTER, null),
ABSTRACT_EVO_ILLU_ILLAGER(ABSTRACT_ILLAGER_BASE, null),
EVOKER(ABSTRACT_EVO_ILLU_ILLAGER),
ILLUSIONER(ABSTRACT_EVO_ILLU_ILLAGER),
VINDICATOR(ABSTRACT_ILLAGER_BASE),
PILLAGER(ABSTRACT_ILLAGER_BASE),
// Skeletons
ABSTRACT_SKELETON(ABSTRACT_MONSTER, null),
SKELETON(ABSTRACT_SKELETON),
STRAY(ABSTRACT_SKELETON),
WITHER_SKELETON(ABSTRACT_SKELETON),
// Guardians
GUARDIAN(ABSTRACT_MONSTER),
ELDER_GUARDIAN(GUARDIAN),
// Spiders
SPIDER(ABSTRACT_MONSTER),
CAVE_SPIDER(SPIDER),
// Zombies
ZOMBIE(ABSTRACT_MONSTER),
DROWNED(ZOMBIE),
HUSK(ZOMBIE),
ZOMBIFIED_PIGLIN(ZOMBIE),
ZOMBIE_VILLAGER(ZOMBIE),
// Flying entities
ABSTRACT_FLYING(ABSTRACT_INSENTIENT, null),
GHAST(ABSTRACT_FLYING),
PHANTOM(ABSTRACT_FLYING),
ABSTRACT_AMBIENT(ABSTRACT_INSENTIENT, null),
BAT(ABSTRACT_AMBIENT),
ALLAY(ABSTRACT_CREATURE),
ABSTRACT_WATERMOB(ABSTRACT_INSENTIENT, null),
SQUID(ABSTRACT_WATERMOB),
GLOW_SQUID(SQUID),
// Slimes
SLIME(ABSTRACT_INSENTIENT),
MAGMA_CUBE(SLIME),
// Hangable objects
ABSTRACT_HANGING(ENTITY, null),
LEASH_KNOT(ABSTRACT_HANGING),
ITEM_FRAME(ABSTRACT_HANGING),
GLOW_ITEM_FRAME(ITEM_FRAME),
PAINTING(ABSTRACT_HANGING),
ABSTRACT_LIGHTNING(ENTITY, null),
LIGHTNING_BOLT(ABSTRACT_LIGHTNING),
// Arrows
ABSTRACT_ARROW(ENTITY, null),
ARROW(ABSTRACT_ARROW),
SPECTRAL_ARROW(ABSTRACT_ARROW),
TRIDENT(ABSTRACT_ARROW),
// Fireballs
ABSTRACT_FIREBALL(ENTITY, null),
DRAGON_FIREBALL(ABSTRACT_FIREBALL),
FIREBALL(ABSTRACT_FIREBALL),
SMALL_FIREBALL(ABSTRACT_FIREBALL),
WITHER_SKULL(ABSTRACT_FIREBALL),
// Projectiles
PROJECTILE_ABSTRACT(ENTITY, null),
SNOWBALL(PROJECTILE_ABSTRACT),
ENDER_PEARL(PROJECTILE_ABSTRACT),
EGG(PROJECTILE_ABSTRACT),
POTION(PROJECTILE_ABSTRACT),
EXPERIENCE_BOTTLE(PROJECTILE_ABSTRACT),
// Vehicles
MINECART_ABSTRACT(ENTITY, null),
CHESTED_MINECART_ABSTRACT(MINECART_ABSTRACT, null),
CHEST_MINECART(CHESTED_MINECART_ABSTRACT),
HOPPER_MINECART(CHESTED_MINECART_ABSTRACT),
MINECART(MINECART_ABSTRACT),
FURNACE_MINECART(MINECART_ABSTRACT),
COMMAND_BLOCK_MINECART(MINECART_ABSTRACT),
TNT_MINECART(MINECART_ABSTRACT),
SPAWNER_MINECART(MINECART_ABSTRACT),
BOAT(ENTITY),
CHEST_BOAT(BOAT);
private static final EntityType[] TYPES = EntityTypeUtil.createSizedArray(values());
private final EntityType parent;
private final String identifier;
private int id = -1;
Entity1_19_3Types(final EntityType parent) {
this.parent = parent;
this.identifier = "minecraft:" + name().toLowerCase(Locale.ROOT);
}
Entity1_19_3Types(final EntityType parent, @Nullable final String identifier) {
this.parent = parent;
this.identifier = identifier;
}
@Override
public int getId() {
if (id == -1) {
throw new IllegalStateException("Ids have not been initialized yet (type " + name() + ")");
}
return id;
}
@Override
public String identifier() {
Preconditions.checkArgument(identifier != null, "Called identifier method on abstract type");
return identifier;
}
@Override
public @Nullable EntityType getParent() {
return parent;
}
@Override
public boolean isAbstractType() {
return identifier == null;
}
public static EntityType getTypeFromId(final int typeId) {
return EntityTypeUtil.getTypeFromId(TYPES, typeId, ENTITY);
}
public static void initialize(final Protocol<?, ?, ?, ?> protocol) {
EntityTypeUtil.initialize(values(), TYPES, protocol, (type, id) -> type.id = id);
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.metadata.types;
import com.viaversion.viaversion.api.minecraft.metadata.MetaType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.ParticleType;
public final class MetaTypes1_19_3 extends AbstractMetaTypes {
public final MetaType byteType = add(0, Type.BYTE);
public final MetaType varIntType = add(1, Type.VAR_INT);
public final MetaType longType = add(2, Type.VAR_LONG);
public final MetaType floatType = add(3, Type.FLOAT);
public final MetaType stringType = add(4, Type.STRING);
public final MetaType componentType = add(5, Type.COMPONENT);
public final MetaType optionalComponentType = add(6, Type.OPTIONAL_COMPONENT);
public final MetaType itemType = add(7, Type.FLAT_VAR_INT_ITEM);
public final MetaType booleanType = add(8, Type.BOOLEAN);
public final MetaType rotationType = add(9, Type.ROTATION);
public final MetaType positionType = add(10, Type.POSITION1_14);
public final MetaType optionalPositionType = add(11, Type.OPTIONAL_POSITION_1_14);
public final MetaType directionType = add(12, Type.VAR_INT);
public final MetaType optionalUUIDType = add(13, Type.OPTIONAL_UUID);
public final MetaType blockStateType = add(14, Type.VAR_INT);
public final MetaType nbtType = add(15, Type.NBT);
public final MetaType particleType;
public final MetaType villagerDatatType = add(17, Type.VILLAGER_DATA);
public final MetaType optionalVarIntType = add(18, Type.OPTIONAL_VAR_INT);
public final MetaType poseType = add(19, Type.VAR_INT);
public final MetaType catVariantType = add(20, Type.VAR_INT);
public final MetaType frogVariantType = add(21, Type.VAR_INT);
public final MetaType optionalGlobalPosition = add(22, Type.OPTIONAL_GLOBAL_POSITION);
public final MetaType paintingVariantType = add(23, Type.VAR_INT);
public MetaTypes1_19_3(final ParticleType particleType) {
super(24);
this.particleType = add(16, particleType);
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.platform;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
public interface ProtocolDetectorService {
/**
* Returns the protocol version of the proxied server, or -1 if unknown.
*
* @param serverName name of the proxied server
* @return protocol version of the proxied server, or -1 if unknown
*/
int serverProtocolVersion(String serverName);
/**
* Probes all registered proxied servers for their protocol version.
* This is executed automatically in the interval set in the ViaVersion config.
*/
void probeAllServers();
/**
* Sets the stored protocol version of a proxied server.
*
* @param serverName name of the proxied server
* @param protocolVersion protocol version of the server
*/
void setProtocolVersion(String serverName, int protocolVersion);
/**
* Uncaches and returns the previously stored protocol version of the proxied server. Returns -1 if none was stored.
*
* @param serverName name of the proxied server
* @return previously stored protocol version of the proxied server, or -1 if none was present
*/
int uncacheProtocolVersion(String serverName);
/**
* Returns an unmodifiable map of detected protocol versions.
*
* @return unmodifiable map of detected protocol versions
*/
Object2IntMap<String> detectedProtocolVersions();
}
@@ -0,0 +1,42 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.platform;
import com.google.common.annotations.Beta;
/**
* Represents a platform that proxies a list of Minecraft servers (Velocity, Bungee).
* A platform might not be a proxy platform, even if {@link ViaPlatform#isProxy()} is true.
*
* @param <T> platform player type
*/
@Beta
public interface ViaServerProxyPlatform<T> extends ViaPlatform<T> {
/**
* Returns a service to get protocol versions for proxied servers.
*
* @return protocol detector service
*/
ProtocolDetectorService protocolDetectorService();
}
@@ -300,7 +300,7 @@ public interface Protocol<C1 extends ClientboundPacketType, C2 extends Clientbou
* <p>
* To be overridden if needed.
*
* @param userConnection The user to initialise
* @param connection user to initialise
*/
default void init(UserConnection connection) {
}
@@ -81,7 +81,8 @@ public class ProtocolVersion {
public static final ProtocolVersion v1_18 = register(757, "1.18/1.18.1", new VersionRange("1.18", 0, 1));
public static final ProtocolVersion v1_18_2 = register(758, "1.18.2");
public static final ProtocolVersion v1_19 = register(759, "1.19");
public static final ProtocolVersion v1_19_1 = register(760, "1.19.1");
public static final ProtocolVersion v1_19_1 = register(760, "1.19.1/2", new VersionRange("1.19", 1, 2));
public static final ProtocolVersion v1_19_3 = register(761, "1.19.3");
public static final ProtocolVersion unknown = register(-1, "UNKNOWN");
public static ProtocolVersion register(int version, String name) {
@@ -87,6 +87,7 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
public static final ByteType BYTE = new ByteType();
public static final UnsignedByteType UNSIGNED_BYTE = new UnsignedByteType();
public static final Type<byte[]> BYTE_ARRAY_PRIMITIVE = new ByteArrayType();
public static final Type<byte[]> OPTIONAL_BYTE_ARRAY_PRIMITIVE = new ByteArrayType.OptionalByteArrayType();
public static final Type<byte[]> SHORT_BYTE_ARRAY = new ShortByteArrayType();
public static final Type<byte[]> REMAINING_BYTES = new RemainingBytesType();
@@ -95,6 +96,7 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
public static final IntType INT = new IntType();
public static final FloatType FLOAT = new FloatType();
public static final FloatType.OptionalFloatType OPTIONAL_FLOAT = new FloatType.OptionalFloatType();
public static final DoubleType DOUBLE = new DoubleType();
public static final LongType LONG = new LongType();
@@ -0,0 +1,56 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.type.types;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import java.util.BitSet;
public class BitSetType extends Type<BitSet> {
private final int length;
private final int bytesLength;
public BitSetType(final int length) {
super(BitSet.class);
this.length = length;
this.bytesLength = -Math.floorDiv(-length, 8); // Ceiled quotient
}
@Override
public BitSet read(ByteBuf buffer) {
final byte[] bytes = new byte[bytesLength];
buffer.readBytes(bytes);
return BitSet.valueOf(bytes);
}
@Override
public void write(ByteBuf buffer, BitSet object) {
Preconditions.checkArgument(object.length() <= length, "BitSet of length " + object.length() + " larger than max length " + length);
final byte[] bytes = object.toByteArray();
buffer.writeBytes(Arrays.copyOf(bytes, bytesLength));
}
}
@@ -23,26 +23,51 @@
package com.viaversion.viaversion.api.type.types;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.type.OptionalType;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
public class ByteArrayType extends Type<byte[]> {
private final int length;
public ByteArrayType(final int length) {
super(byte[].class);
this.length = length;
}
public ByteArrayType() {
super(byte[].class);
this.length = -1;
}
@Override
public void write(ByteBuf buffer, byte[] object) throws Exception {
Type.VAR_INT.writePrimitive(buffer, object.length);
public void write(final ByteBuf buffer, final byte[] object) throws Exception {
if (this.length != -1) {
Preconditions.checkArgument(length == object.length, "Length does not match expected length");
} else {
Type.VAR_INT.writePrimitive(buffer, object.length);
}
buffer.writeBytes(object);
}
@Override
public byte[] read(ByteBuf buffer) throws Exception {
int length = Type.VAR_INT.readPrimitive(buffer);
public byte[] read(final ByteBuf buffer) throws Exception {
final int length = this.length == -1 ? Type.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
byte[] array = new byte[length];
final byte[] array = new byte[length];
buffer.readBytes(array);
return array;
}
public static final class OptionalByteArrayType extends OptionalType<byte[]> {
public OptionalByteArrayType() {
super(Type.BYTE_ARRAY_PRIMITIVE);
}
public OptionalByteArrayType(final int length) {
super(new ByteArrayType(length));
}
}
}
@@ -22,6 +22,7 @@
*/
package com.viaversion.viaversion.api.type.types;
import com.viaversion.viaversion.api.type.OptionalType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.TypeConverter;
import io.netty.buffer.ByteBuf;
@@ -67,4 +68,11 @@ public class FloatType extends Type<Float> implements TypeConverter<Float> {
}
return (Float) o;
}
public static final class OptionalFloatType extends OptionalType<Float> {
public OptionalFloatType() {
super(Type.FLOAT);
}
}
}
@@ -24,7 +24,7 @@ package com.viaversion.viaversion.api.type.types.minecraft;
import com.github.steveice10.opennbt.NBTIO;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.common.base.Preconditions;
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
@@ -35,21 +35,22 @@ import java.io.DataOutput;
public class NBTType extends Type<CompoundTag> {
private static final int MAX_NBT_BYTES = 2097152; // 2mb
private static final int MAX_NESTING_LEVEL = 512;
public NBTType() {
super(CompoundTag.class);
}
@Override
public CompoundTag read(ByteBuf buffer) throws Exception {
Preconditions.checkArgument(buffer.readableBytes() <= 2097152, "Cannot read NBT (got %s bytes)", buffer.readableBytes());
int readerIndex = buffer.readerIndex();
byte b = buffer.readByte();
if (b == 0) {
return null;
} else {
buffer.readerIndex(readerIndex);
return NBTIO.readTag((DataInput) new ByteBufInputStream(buffer));
return NBTIO.readTag((DataInput) new ByteBufInputStream(buffer), TagLimiter.create(MAX_NBT_BYTES, MAX_NESTING_LEVEL));
}
}
@@ -24,6 +24,8 @@ package com.viaversion.viaversion.api.type.types.version;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
@@ -38,16 +40,14 @@ public class ChunkSectionType1_8 extends Type<ChunkSection> {
@Override
public ChunkSection read(ByteBuf buffer) throws Exception {
ChunkSection chunkSection = new ChunkSectionImpl(true);
DataPalette blocks = chunkSection.palette(PaletteType.BLOCKS);
// 0 index needs to be air in 1.9
chunkSection.addPaletteEntry(0);
blocks.addId(0);
ByteBuf littleEndianView = buffer.order(ByteOrder.LITTLE_ENDIAN);
for (int i = 0; i < ChunkSection.SIZE; i++) {
int mask = littleEndianView.readShort();
int type = mask >> 4;
int data = mask & 0xF;
chunkSection.setBlockWithData(i, type, data);
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
blocks.setIdAt(idx, littleEndianView.readShort());
}
return chunkSection;
@@ -55,15 +55,11 @@ public class ChunkSectionType1_8 extends Type<ChunkSection> {
@Override
public void write(ByteBuf buffer, ChunkSection chunkSection) throws Exception {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = chunkSection.getFlatBlock(x, y, z);
buffer.writeByte(block);
buffer.writeByte(block >> 8);
}
}
DataPalette blocks = chunkSection.palette(PaletteType.BLOCKS);
ByteBuf littleEndianView = buffer.order(ByteOrder.LITTLE_ENDIAN);
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
littleEndianView.writeShort(blocks.idAt(idx));
}
}
}
@@ -0,0 +1,39 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.type.types.version;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import com.viaversion.viaversion.api.minecraft.metadata.types.MetaTypes1_19_3;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.MetaListType;
import com.viaversion.viaversion.api.type.types.minecraft.ParticleType;
import java.util.List;
public final class Types1_19_3 {
public static final ParticleType PARTICLE = new ParticleType(); // Only safe to use after protocol loading
public static final MetaTypes1_19_3 META_TYPES = new MetaTypes1_19_3(PARTICLE);
public static final Type<Metadata> METADATA = new MetadataType(META_TYPES);
public static final Type<List<Metadata>> METADATA_LIST = new MetaListType(METADATA);
}
@@ -48,7 +48,7 @@ public class InformativeException extends Exception {
@Override
public String getMessage() {
StringBuilder builder = new StringBuilder("Please post this error to https://github.com/ViaVersion/ViaVersion/issues and follow the issue template\n{");
StringBuilder builder = new StringBuilder("Please report this on the Via support Discord or open an issue on the relevant GitHub repository\n");
boolean first = true;
for (Map.Entry<String, Object> entry : info.entrySet()) {
if (!first) {
@@ -57,11 +57,11 @@ public class InformativeException extends Exception {
builder.append(entry.getKey()).append(": ").append(entry.getValue());
first = false;
}
return builder.append("}\nActual Error: ").toString();
return builder.toString();
}
@Override
public synchronized Throwable fillInStackTrace() {
public Throwable fillInStackTrace() {
// Don't record this stack
return this;
}
@@ -24,29 +24,28 @@ package com.viaversion.viaversion.util;
import java.util.function.IntToLongFunction;
public class CompactArrayUtil {
//Oh no
private static final int[] MAGIC = {
-1, -1, 0, Integer.MIN_VALUE, 0, 0, 1431655765, 1431655765, 0, Integer.MIN_VALUE,
0, 1, 858993459, 858993459, 0, 715827882, 715827882, 0, 613566756, 613566756,
0, Integer.MIN_VALUE, 0, 2, 477218588, 477218588, 0, 429496729, 429496729, 0,
390451572, 390451572, 0, 357913941, 357913941, 0, 330382099, 330382099, 0, 306783378,
306783378, 0, 286331153, 286331153, 0, Integer.MIN_VALUE, 0, 3, 252645135, 252645135,
0, 238609294, 238609294, 0, 226050910, 226050910, 0, 214748364, 214748364, 0,
204522252, 204522252, 0, 195225786, 195225786, 0, 186737708, 186737708, 0, 178956970,
178956970, 0, 171798691, 171798691, 0, 165191049, 165191049, 0, 159072862, 159072862,
0, 153391689, 153391689, 0, 148102320, 148102320, 0, 143165576, 143165576, 0,
138547332, 138547332, 0, Integer.MIN_VALUE, 0, 4, 130150524, 130150524, 0, 126322567,
126322567, 0, 122713351, 122713351, 0, 119304647, 119304647, 0, 116080197, 116080197,
0, 113025455, 113025455, 0, 110127366, 110127366, 0, 107374182, 107374182, 0,
104755299, 104755299, 0, 102261126, 102261126, 0, 99882960, 99882960, 0, 97612893,
97612893, 0, 95443717, 95443717, 0, 93368854, 93368854, 0, 91382282, 91382282,
0, 89478485, 89478485, 0, 87652393, 87652393, 0, 85899345, 85899345, 0,
84215045, 84215045, 0, 82595524, 82595524, 0, 81037118, 81037118, 0, 79536431,
79536431, 0, 78090314, 78090314, 0, 76695844, 76695844, 0, 75350303, 75350303,
0, 74051160, 74051160, 0, 72796055, 72796055, 0, 71582788, 71582788, 0,
70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE,
0, 5};
public final class CompactArrayUtil {
private static final long[] RECIPROCAL_MULT_AND_ADD = {
0xffffffffL, 0x00000000, 0x55555555, 0x00000000, 0x33333333, 0x2aaaaaaa, 0x24924924, 0x00000000,
0x1c71c71c, 0x19999999, 0x1745d174, 0x15555555, 0x13b13b13, 0x12492492, 0x11111111, 0x00000000,
0xf0f0f0f, 0xe38e38e, 0xd79435e, 0xccccccc, 0xc30c30c, 0xba2e8ba, 0xb21642c, 0xaaaaaaa,
0xa3d70a3, 0x9d89d89, 0x97b425e, 0x9249249, 0x8d3dcb0, 0x8888888, 0x8421084, 0x00000000,
0x7c1f07c, 0x7878787, 0x7507507, 0x71c71c7, 0x6eb3e45, 0x6bca1af, 0x6906906, 0x6666666,
0x63e7063, 0x6186186, 0x5f417d0, 0x5d1745d, 0x5b05b05, 0x590b216, 0x572620a, 0x5555555,
0x5397829, 0x51eb851, 0x5050505, 0x4ec4ec4, 0x4d4873e, 0x4bda12f, 0x4a7904a, 0x4924924,
0x47dc11f, 0x469ee58, 0x456c797, 0x4444444, 0x4325c53, 0x4210842, 0x4104104, 0x00000000
};
// Incrementally shift with each 0x80000000/0x00000000 tuple
private static final int[] RECIPROCAL_RIGHT_SHIFT = {
0, 0, 0, 1, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 4,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 5
};
private CompactArrayUtil() {
throw new AssertionError();
@@ -55,10 +54,10 @@ public class CompactArrayUtil {
public static long[] createCompactArrayWithPadding(int bitsPerEntry, int entries, IntToLongFunction valueGetter) {
long maxEntryValue = (1L << bitsPerEntry) - 1;
char valuesPerLong = (char) (64 / bitsPerEntry);
int magicIndex = 3 * (valuesPerLong - 1);
long divideMul = Integer.toUnsignedLong(MAGIC[magicIndex]);
long divideAdd = Integer.toUnsignedLong(MAGIC[magicIndex + 1]);
int divideShift = MAGIC[magicIndex + 2];
int magicIndex = valuesPerLong - 1;
long divideAdd = RECIPROCAL_MULT_AND_ADD[magicIndex];
long divideMul = divideAdd != 0 ? divideAdd : 0x80000000L; // Special case for the 0x80000000/0x00000000 tuple
int divideShift = RECIPROCAL_RIGHT_SHIFT[magicIndex];
int size = (entries + valuesPerLong - 1) / valuesPerLong;
long[] data = new long[size];
@@ -76,10 +75,10 @@ public class CompactArrayUtil {
public static void iterateCompactArrayWithPadding(int bitsPerEntry, int entries, long[] data, BiIntConsumer consumer) {
long maxEntryValue = (1L << bitsPerEntry) - 1;
char valuesPerLong = (char) (64 / bitsPerEntry);
int magicIndex = 3 * (valuesPerLong - 1);
long divideMul = Integer.toUnsignedLong(MAGIC[magicIndex]);
long divideAdd = Integer.toUnsignedLong(MAGIC[magicIndex + 1]);
int divideShift = MAGIC[magicIndex + 2];
int magicIndex = valuesPerLong - 1;
long divideAdd = RECIPROCAL_MULT_AND_ADD[magicIndex];
long divideMul = divideAdd != 0 ? divideAdd : 0x80000000L;
int divideShift = RECIPROCAL_RIGHT_SHIFT[magicIndex];
for (int i = 0; i < entries; i++) {
int cellIndex = (int) (i * divideMul + divideAdd >> 32L >> divideShift);
@@ -22,14 +22,18 @@
*/
package com.viaversion.viaversion.util;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.protocol.Protocol;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class EntityTypeUtil {
public final class EntityTypeUtil {
private static final EntityType[] EMPTY_ARRAY = new EntityType[0];
/**
* Returns an ordered array with each index representing the actual entity id.
@@ -46,7 +50,29 @@ public class EntityTypeUtil {
}
types.sort(Comparator.comparingInt(EntityType::getId));
return types.toArray(new EntityType[0]);
return types.toArray(EMPTY_ARRAY);
}
/**
* Sets entity type ids based on the protocol's mapping data and fills the given typesToFill array with the index corresponding to the id.
*
* @param values full enum values
* @param typesToFill yet unfilled array to be filled with types ordered by id
* @param protocol protocol to get entity types from
* @param idSetter function to set the internal entity id
* @param <T> entity type
*/
public static <T extends EntityType> void initialize(final T[] values, final EntityType[] typesToFill, final Protocol<?, ?, ?, ?> protocol, final EntityIdSetter<T> idSetter) {
for (final T type : values) {
if (type.isAbstractType()) {
continue;
}
final int id = protocol.getMappingData().getEntityMappings().mappedId(type.identifier());
Preconditions.checkArgument(id != -1, "Entity type %s has no id", type.identifier());
idSetter.setId(type, id);
typesToFill[id] = type;
}
}
public static EntityType[] createSizedArray(final EntityType[] values) {
@@ -56,15 +82,7 @@ public class EntityTypeUtil {
count++;
}
}
return new EntityType[(int) count];
}
public static void fill(final EntityType[] values, final EntityType[] toFill) {
for (final EntityType type : values) {
if (!type.isAbstractType()) {
toFill[type.getId()] = type;
}
}
return new EntityType[count];
}
/**
@@ -83,4 +101,10 @@ public class EntityTypeUtil {
}
return type;
}
@FunctionalInterface
public interface EntityIdSetter<T extends EntityType> {
void setId(T entityType, int id);
}
}
@@ -34,29 +34,21 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class PipelineUtil {
private static Method DECODE_METHOD;
private static Method ENCODE_METHOD;
private static Method MTM_DECODE;
public final class PipelineUtil {
private static final Method DECODE_METHOD;
private static final Method ENCODE_METHOD;
private static final Method MTM_DECODE;
static {
try {
DECODE_METHOD = ByteToMessageDecoder.class.getDeclaredMethod("decode", ChannelHandlerContext.class, ByteBuf.class, List.class);
DECODE_METHOD.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
ENCODE_METHOD = MessageToByteEncoder.class.getDeclaredMethod("encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
ENCODE_METHOD.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
MTM_DECODE = MessageToMessageDecoder.class.getDeclaredMethod("decode", ChannelHandlerContext.class, Object.class, List.class);
MTM_DECODE.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@@ -25,7 +25,6 @@ tasks {
publishShadowJar()
fun ShadowJar.configureRelocations() {
relocate("javassist", "com.viaversion.viaversion.libs.javassist")
relocate("com.google.gson", "com.viaversion.viaversion.libs.gson")
relocate("com.github.steveice10.opennbt", "com.viaversion.viaversion.libs.opennbt")
relocate("it.unimi.dsi.fastutil", "com.viaversion.viaversion.libs.fastutil")
+1 -1
View File
@@ -1,10 +1,10 @@
dependencies {
implementation(projects.viaversionBukkitLegacy)
implementation(projects.viaversionCommon)
implementation(libs.javassist)
compileOnly(libs.paper) {
exclude("junit", "junit")
exclude("com.google.code.gson", "gson")
exclude("javax.persistence", "persistence-api")
}
compileOnly(projects.compat.protocolsupportCompat)
}
@@ -26,16 +26,13 @@ import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.platform.PlatformTask;
import com.viaversion.viaversion.api.platform.UnsupportedSoftware;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
import com.viaversion.viaversion.bukkit.commands.BukkitCommandHandler;
import com.viaversion.viaversion.bukkit.commands.BukkitCommandSender;
import com.viaversion.viaversion.bukkit.listeners.ProtocolLibEnableListener;
import com.viaversion.viaversion.bukkit.platform.BukkitViaAPI;
import com.viaversion.viaversion.bukkit.platform.BukkitViaConfig;
import com.viaversion.viaversion.bukkit.platform.BukkitViaInjector;
import com.viaversion.viaversion.bukkit.platform.BukkitViaLoader;
import com.viaversion.viaversion.bukkit.platform.BukkitViaTask;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.dump.PluginInfo;
import com.viaversion.viaversion.unsupported.UnsupportedPlugin;
import com.viaversion.viaversion.unsupported.UnsupportedServerSoftware;
@@ -60,8 +57,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
private final List<Runnable> queuedTasks = new ArrayList<>();
private final List<Runnable> asyncQueuedTasks = new ArrayList<>();
private final boolean protocolSupport;
private boolean compatSpigotBuild;
private boolean spigot = true;
private boolean lateBind;
public ViaVersionPlugin() {
@@ -89,37 +84,13 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
@Override
public void onLoad() {
// Via should load before PL, so we can't check for it in the constructor
Plugin protocolLib = Bukkit.getPluginManager().getPlugin("ProtocolLib");
ProtocolLibEnableListener.checkCompat(protocolLib);
// Spigot detector
try {
Class.forName("org.spigotmc.SpigotConfig");
} catch (ClassNotFoundException e) {
spigot = false;
}
// Check if it's a spigot build with a protocol mod
try {
NMSUtil.nms(
"PacketEncoder",
"net.minecraft.network.PacketEncoder"
).getDeclaredField("version");
compatSpigotBuild = true;
} catch (Exception e) {
compatSpigotBuild = false;
}
if (getServer().getPluginManager().getPlugin("ViaBackwards") != null) {
MappingDataLoader.enableMappingsCache();
}
// Generate classes needed (only works if it's compat or ps)
ClassGenerator.generate();
lateBind = !((BukkitViaInjector) Via.getManager().getInjector()).isBinded();
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
if (!lateBind) {
((ViaManagerImpl) Via.getManager()).init();
}
@@ -134,10 +105,8 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
getCommand("viaversion").setExecutor(commandHandler);
getCommand("viaversion").setTabCompleter(commandHandler);
getServer().getPluginManager().registerEvents(new ProtocolLibEnableListener(), this);
// Warn them if they have anti-xray on and they aren't using spigot
if (conf.isAntiXRay() && !spigot) {
if (conf.isAntiXRay() && !isSpigot()) {
getLogger().info("You have anti-xray on in your config, since you're not using spigot it won't fix xray!");
}
@@ -286,7 +255,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
@Override
public final Collection<UnsupportedSoftware> getUnsupportedSoftwareClasses() {
List<UnsupportedSoftware> list = new ArrayList<>(ViaPlatform.super.getUnsupportedSoftwareClasses());
final List<UnsupportedSoftware> list = new ArrayList<>(ViaPlatform.super.getUnsupportedSoftwareClasses());
list.add(new UnsupportedServerSoftware.Builder().name("Yatopia").reason(UnsupportedServerSoftware.Reason.DANGEROUS_SERVER_SOFTWARE)
.addClassName("org.yatopiamc.yatopia.server.YatopiaConfig")
.addClassName("net.yatopia.api.event.PlayerAttackEntityEvent")
@@ -307,18 +276,19 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
return lateBind;
}
public boolean isCompatSpigotBuild() {
return compatSpigotBuild;
}
public boolean isSpigot() {
return this.spigot;
}
public boolean isProtocolSupport() {
return protocolSupport;
}
private boolean isSpigot() {
try {
Class.forName("org.spigotmc.SpigotConfig");
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
public static ViaVersionPlugin getInstance() {
return instance;
}
@@ -1,363 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.classgenerator;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.bukkit.handlers.BukkitDecodeHandler;
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.classgenerator.generated.BasicHandlerConstructor;
import com.viaversion.viaversion.classgenerator.generated.HandlerConstructor;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;
import javassist.expr.ConstructorCall;
import javassist.expr.ExprEditor;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.reflect.Method;
//TODO maybe clean this up a bit 👀
public final class ClassGenerator {
private static final boolean useModules = hasModuleMethod();
private static HandlerConstructor constructor = new BasicHandlerConstructor();
private static String psPackage;
private static Class psConnectListener;
public static HandlerConstructor getConstructor() {
return constructor;
}
public static void generate() {
if (ViaVersionPlugin.getInstance().isCompatSpigotBuild() || ViaVersionPlugin.getInstance().isProtocolSupport()) {
try {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new LoaderClassPath(Bukkit.class.getClassLoader()));
for (Plugin p : Bukkit.getPluginManager().getPlugins()) {
pool.insertClassPath(new LoaderClassPath(p.getClass().getClassLoader()));
}
if (ViaVersionPlugin.getInstance().isCompatSpigotBuild()) {
Class decodeSuper = NMSUtil.nms("PacketDecoder", "net.minecraft.network.PacketDecoder");
Class encodeSuper = NMSUtil.nms("PacketEncoder", "net.minecraft.network.PacketEncoder");
// Generate the classes
addSpigotCompatibility(pool, BukkitDecodeHandler.class, decodeSuper);
addSpigotCompatibility(pool, BukkitEncodeHandler.class, encodeSuper);
} else {
// ProtocolSupport compatibility
Class encodeSuper;
Class decodeSuper;
if (isMultiplatformPS()) {
psConnectListener = makePSConnectListener(pool);
return;
} else {
String psPackage = getOldPSPackage();
decodeSuper = Class.forName(psPackage.equals("unknown") ? "protocolsupport.protocol.pipeline.common.PacketDecoder" : psPackage + ".wrapped.WrappedDecoder");
encodeSuper = Class.forName(psPackage.equals("unknown") ? "protocolsupport.protocol.pipeline.common.PacketEncoder" : psPackage + ".wrapped.WrappedEncoder");
}
// Generate the classes
addPSCompatibility(pool, BukkitDecodeHandler.class, decodeSuper);
addPSCompatibility(pool, BukkitEncodeHandler.class, encodeSuper);
}
// Implement Constructor
CtClass generated = pool.makeClass("com.viaversion.viaversion.classgenerator.generated.GeneratedConstructor");
CtClass handlerInterface = pool.get(HandlerConstructor.class.getName());
generated.setInterfaces(new CtClass[]{handlerInterface});
// Import required classes
pool.importPackage("com.viaversion.viaversion.classgenerator.generated");
pool.importPackage("com.viaversion.viaversion.classgenerator");
pool.importPackage("com.viaversion.viaversion.api.connection");
pool.importPackage("io.netty.handler.codec");
// Implement Methods
generated.addMethod(CtMethod.make("public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {\n" +
" return new BukkitEncodeHandler(info, minecraftEncoder);\n" +
" }", generated));
generated.addMethod(CtMethod.make("public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {\n" +
" return new BukkitDecodeHandler(info, minecraftDecoder);\n" +
" }", generated));
constructor = (HandlerConstructor) toClass(generated).getConstructor().newInstance();
} catch (ReflectiveOperationException | CannotCompileException | NotFoundException e) {
e.printStackTrace();
}
}
}
private static void addSpigotCompatibility(ClassPool pool, Class input, Class superclass) {
String newName = "com.viaversion.viaversion.classgenerator.generated." + input.getSimpleName();
try {
CtClass generated = pool.getAndRename(input.getName(), newName);
if (superclass != null) {
CtClass toExtend = pool.get(superclass.getName());
generated.setSuperclass(toExtend);
// If it's NMS satisfy constructor.
if (superclass.getName().startsWith("net.minecraft")) {
// Modify constructor to call super
if (generated.getConstructors().length != 0) {
generated.getConstructors()[0].instrument(new ExprEditor() {
@Override
public void edit(ConstructorCall c) throws CannotCompileException {
if (c.isSuper()) {
// Constructor for both has a stats thing.
c.replace("super(null);");
}
super.edit(c);
}
});
}
}
}
toClass(generated);
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
}
}
private static void addPSCompatibility(ClassPool pool, Class input, Class superclass) {
boolean newPS = getOldPSPackage().equals("unknown");
String newName = "com.viaversion.viaversion.classgenerator.generated." + input.getSimpleName();
try {
CtClass generated = pool.getAndRename(input.getName(), newName);
if (superclass != null) {
CtClass toExtend = pool.get(superclass.getName());
generated.setSuperclass(toExtend);
if (!newPS) {
// Override setRealEncoder / setRealDecoder
pool.importPackage(getOldPSPackage());
pool.importPackage(getOldPSPackage() + ".wrapped");
if (superclass.getName().endsWith("Decoder")) {
// Decoder
generated.addMethod(CtMethod.make("public void setRealDecoder(IPacketDecoder dec) {\n" +
" ((WrappedDecoder) this.minecraftDecoder).setRealDecoder(dec);\n" +
" }", generated));
} else {
// Encoder
pool.importPackage("protocolsupport.api");
pool.importPackage("java.lang.reflect");
generated.addMethod(CtMethod.make("public void setRealEncoder(IPacketEncoder enc) {\n" +
" try {\n" +
// Tell ProtocolSupport to decode MINECRAFT_FUTURE packets using the default decoder (for 1.9.4)
" Field field = enc.getClass().getDeclaredField(\"version\");\n" +
" field.setAccessible(true);\n" +
" ProtocolVersion version = (ProtocolVersion) field.get(enc);\n" +
" if (version == ProtocolVersion.MINECRAFT_FUTURE) enc = enc.getClass().getConstructor(\n" +
" new Class[]{ProtocolVersion.class}).newInstance(new Object[] {ProtocolVersion.getLatest()});\n" +
" } catch (Exception e) {\n" +
// I guess we're not on 1.9.4
" }\n" +
" ((WrappedEncoder) this.minecraftEncoder).setRealEncoder(enc);\n" +
" }", generated));
}
}
}
toClass(generated);
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
}
}
private static Class makePSConnectListener(ClassPool pool) {
HandshakeProtocolType type = handshakeVersionMethod();
try {
// Reference classes
CtClass toExtend = pool.get("protocolsupport.api.Connection$PacketListener");
CtClass connectListenerClazz = pool.makeClass("com.viaversion.viaversion.classgenerator.generated.ProtocolSupportConnectListener");
connectListenerClazz.setSuperclass(toExtend);
// Import packages
pool.importPackage("java.util.Arrays");
pool.importPackage("protocolsupport.api.ProtocolVersion");
pool.importPackage("protocolsupport.api.ProtocolType");
pool.importPackage("protocolsupport.api.Connection");
pool.importPackage("protocolsupport.api.Connection.PacketListener");
pool.importPackage("protocolsupport.api.Connection.PacketListener.PacketEvent");
pool.importPackage("protocolsupport.protocol.ConnectionImpl");
pool.importPackage(NMSUtil.nms(
"PacketHandshakingInSetProtocol",
"net.minecraft.network.protocol.handshake.PacketHandshakingInSetProtocol"
).getName());
// Add connection reference field
connectListenerClazz.addField(CtField.make("private ConnectionImpl connection;", connectListenerClazz));
// Bake constructor
connectListenerClazz.addConstructor(CtNewConstructor.make(
"public ProtocolSupportConnectListener (ConnectionImpl connection) {\n"
+ " this.connection = connection;\n"
+ "}", connectListenerClazz));
// Add the listening method
connectListenerClazz.addMethod(CtNewMethod.make(
// On packet receive
"public void onPacketReceiving(protocolsupport.api.Connection.PacketListener.PacketEvent event) {\n"
// Check if we are getting handshake packet.
+ " if (event.getPacket() instanceof PacketHandshakingInSetProtocol) {\n"
// Get protocol version.
+ " PacketHandshakingInSetProtocol packet = (PacketHandshakingInSetProtocol) event.getPacket();\n"
+ " int protoVersion = packet." + type.methodName() + "();\n"
// ViaVersion has at this point already spoofed the connectionversion. (Since it is higher up the pipeline)
// If via has put the protoVersion to the server we can spoof ProtocolSupport's version.
+ " if (connection.getVersion() == ProtocolVersion.MINECRAFT_FUTURE && protoVersion == com.viaversion.viaversion.api.Via.getAPI().getServerVersion().lowestSupportedVersion()) {\n"
+ " connection.setVersion(ProtocolVersion.getLatest(ProtocolType.PC));\n"
+ " }\n"
+ " }\n"
// Id version is not serverversion viaversion will not spoof. ProtocolSupport will handle the rest.
// In any case, remove the packet listener and wrap up.
+ " connection.removePacketListener(this);\n"
+ "}", connectListenerClazz));
return toClass(connectListenerClazz);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void registerPSConnectListener(ViaVersionPlugin plugin) {
if (psConnectListener != null) {
try {
Class<? extends Event> connectionOpenEvent = (Class<? extends Event>) Class.forName("protocolsupport.api.events.ConnectionOpenEvent");
Bukkit.getPluginManager().registerEvent(connectionOpenEvent, new Listener() {
}, EventPriority.HIGH, new EventExecutor() {
@Override
public void execute(@NonNull Listener listener, @NonNull Event event) throws EventException {
try {
Object connection = event.getClass().getMethod("getConnection").invoke(event);
Object connectListener = psConnectListener.getConstructor(connection.getClass()).newInstance(connection);
Method addConnectListener = connection.getClass().getMethod("addPacketListener", Class.forName("protocolsupport.api.Connection$PacketListener"));
addConnectListener.invoke(connection, connectListener);
} catch (Exception e) {
e.printStackTrace();
}
}
}, plugin);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static Class getPSConnectListener() {
return psConnectListener;
}
public static String getOldPSPackage() {
if (psPackage == null) {
try {
Class.forName("protocolsupport.protocol.core.IPacketDecoder");
psPackage = "protocolsupport.protocol.core";
} catch (ClassNotFoundException e) {
try {
Class.forName("protocolsupport.protocol.pipeline.IPacketDecoder");
psPackage = "protocolsupport.protocol.pipeline";
} catch (ClassNotFoundException e1) {
psPackage = "unknown";
}
}
}
return psPackage;
}
public static boolean isMultiplatformPS() {
try {
Class.forName("protocolsupport.zplatform.impl.spigot.network.pipeline.SpigotPacketEncoder");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
public static HandshakeProtocolType handshakeVersionMethod() {
Class<?> clazz = null;
// Check for the mapped method
try {
clazz = NMSUtil.nms(
"PacketHandshakingInSetProtocol",
"net.minecraft.network.protocol.handshake.PacketHandshakingInSetProtocol"
);
clazz.getMethod("getProtocolVersion");
return HandshakeProtocolType.MAPPED;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException ignored) {
}
// Check for obfusacted b/c methods
try {
if (clazz.getMethod("b").getReturnType() == int.class) {
return HandshakeProtocolType.OBFUSCATED_OLD;
} else if (clazz.getMethod("c").getReturnType() == int.class) {
return HandshakeProtocolType.OBFUSCATED_NEW;
}
throw new UnsupportedOperationException("Protocol version method not found in " + clazz.getSimpleName());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("deprecation")
private static Class<?> toClass(CtClass ctClass) throws CannotCompileException {
return useModules ? ctClass.toClass(HandlerConstructor.class) : ctClass.toClass(HandlerConstructor.class.getClassLoader());
}
private static boolean hasModuleMethod() {
try {
Class.class.getDeclaredMethod("getModule");
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
private enum HandshakeProtocolType {
MAPPED("getProtocolVersion"),
OBFUSCATED_OLD("b"),
OBFUSCATED_NEW("c");
private final String methodName;
HandshakeProtocolType(String methodName) {
this.methodName = methodName;
}
public String methodName() {
return methodName;
}
}
}
@@ -0,0 +1,105 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.compat;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import java.util.logging.Level;
public final class ProtocolSupportCompat {
public static void registerPSConnectListener(ViaVersionPlugin plugin) {
Via.getPlatform().getLogger().info("Registering ProtocolSupport compat connection listener");
try {
//noinspection unchecked
Class<? extends Event> connectionOpenEvent = (Class<? extends Event>) Class.forName("protocolsupport.api.events.ConnectionOpenEvent");
Bukkit.getPluginManager().registerEvent(connectionOpenEvent, new Listener() {
}, EventPriority.HIGH, (listener, event) -> {
try {
Object connection = event.getClass().getMethod("getConnection").invoke(event);
ProtocolSupportConnectionListener connectListener = new ProtocolSupportConnectionListener(connection);
ProtocolSupportConnectionListener.ADD_PACKET_LISTENER_METHOD.invoke(connection, connectListener);
} catch (ReflectiveOperationException e) {
Via.getPlatform().getLogger().log(Level.WARNING, "Error when handling ProtocolSupport event", e);
}
}, plugin);
} catch (ClassNotFoundException e) {
Via.getPlatform().getLogger().log(Level.WARNING, "Unable to register ProtocolSupport listener", e);
}
}
public static boolean isMultiplatformPS() {
try {
Class.forName("protocolsupport.zplatform.impl.spigot.network.pipeline.SpigotPacketEncoder");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
static HandshakeProtocolType handshakeVersionMethod() {
Class<?> clazz = null;
// Check for the mapped method
try {
clazz = NMSUtil.nms(
"PacketHandshakingInSetProtocol",
"net.minecraft.network.protocol.handshake.PacketHandshakingInSetProtocol"
);
clazz.getMethod("getProtocolVersion");
return HandshakeProtocolType.MAPPED;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException ignored) {
}
// Check for obfusacted b/c methods
try {
if (clazz.getMethod("b").getReturnType() == int.class) {
return HandshakeProtocolType.OBFUSCATED_B;
} else if (clazz.getMethod("c").getReturnType() == int.class) {
return HandshakeProtocolType.OBFUSCATED_C;
}
throw new UnsupportedOperationException("Protocol version method not found in " + clazz.getSimpleName());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
enum HandshakeProtocolType {
MAPPED("getProtocolVersion"),
OBFUSCATED_B("b"),
OBFUSCATED_C("c");
private final String methodName;
HandshakeProtocolType(String methodName) {
this.methodName = methodName;
}
public String methodName() {
return methodName;
}
}
}
@@ -0,0 +1,88 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.compat;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import protocolsupport.api.Connection;
import java.lang.reflect.Method;
final class ProtocolSupportConnectionListener extends Connection.PacketListener {
static final Method ADD_PACKET_LISTENER_METHOD;
private static final Class<?> HANDSHAKE_PACKET_CLASS;
private static final Method GET_VERSION_METHOD;
private static final Method SET_VERSION_METHOD;
private static final Method REMOVE_PACKET_LISTENER_METHOD;
private static final Method GET_LATEST_METHOD;
private static final Object PROTOCOL_VERSION_MINECRAFT_FUTURE;
private static final Object PROTOCOL_TYPE_PC;
static {
try {
HANDSHAKE_PACKET_CLASS = NMSUtil.nms(
"PacketHandshakingInSetProtocol",
"net.minecraft.network.protocol.handshake.PacketHandshakingInSetProtocol"
);
final Class<?> connectionImplClass = Class.forName("protocolsupport.protocol.ConnectionImpl");
final Class<?> connectionClass = Class.forName("protocolsupport.api.Connection");
final Class<?> packetListenerClass = Class.forName("protocolsupport.api.Connection$PacketListener");
final Class<?> protocolVersionClass = Class.forName("protocolsupport.api.ProtocolVersion");
final Class<?> protocolTypeClass = Class.forName("protocolsupport.api.ProtocolType");
GET_VERSION_METHOD = connectionClass.getDeclaredMethod("getVersion");
SET_VERSION_METHOD = connectionImplClass.getDeclaredMethod("setVersion", protocolVersionClass);
PROTOCOL_VERSION_MINECRAFT_FUTURE = protocolVersionClass.getDeclaredField("MINECRAFT_FUTURE").get(null);
GET_LATEST_METHOD = protocolVersionClass.getDeclaredMethod("getLatest", protocolTypeClass);
PROTOCOL_TYPE_PC = protocolTypeClass.getDeclaredField("PC").get(null);
ADD_PACKET_LISTENER_METHOD = connectionClass.getDeclaredMethod("addPacketListener", packetListenerClass);
REMOVE_PACKET_LISTENER_METHOD = connectionClass.getDeclaredMethod("removePacketListener", packetListenerClass);
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private final Object connection;
ProtocolSupportConnectionListener(final Object connection) {
this.connection = connection;
}
@Override
public void onPacketReceiving(final Connection.PacketListener.PacketEvent event) {
try {
// Check if we are getting handshake packet.
if (HANDSHAKE_PACKET_CLASS.isInstance(event.getPacket()) && GET_VERSION_METHOD.invoke(connection) == PROTOCOL_VERSION_MINECRAFT_FUTURE) {
final Object packet = event.getPacket();
final int protocolVersion = (int) HANDSHAKE_PACKET_CLASS.getDeclaredMethod(ProtocolSupportCompat.handshakeVersionMethod().methodName()).invoke(packet);
// ViaVersion has at this point already spoofed the connectionversion. (Since it is higher up the pipeline)
// If via has put the protoVersion to the server we can spoof ProtocolSupport's version.
if (protocolVersion == Via.getAPI().getServerVersion().lowestSupportedVersion()) {
SET_VERSION_METHOD.invoke(connection, GET_LATEST_METHOD.invoke(null, PROTOCOL_TYPE_PC));
}
}
// Id version is not serverversion viaversion will not spoof. ProtocolSupport will handle the rest.
// In any case, remove the packet listener and wrap up.
REMOVE_PACKET_LISTENER_METHOD.invoke(connection, this);
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
@@ -18,33 +18,48 @@
package com.viaversion.viaversion.bukkit.handlers;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
import com.viaversion.viaversion.classgenerator.generated.HandlerConstructor;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.channel.ChannelPipeline;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Method;
public class BukkitChannelInitializer extends ChannelInitializer<Channel> implements WrappedChannelInitializer {
public final class BukkitChannelInitializer extends ChannelInitializer<Channel> implements WrappedChannelInitializer {
public static final String VIA_ENCODER = "via-encoder";
public static final String VIA_DECODER = "via-decoder";
public static final String MINECRAFT_ENCODER = "encoder";
public static final String MINECRAFT_DECODER = "decoder";
public static final String MINECRAFT_COMPRESSOR = "compress";
public static final String MINECRAFT_DECOMPRESSOR = "decompress";
public static final Object COMPRESSION_ENABLED_EVENT = paperCompressionEnabledEvent();
private static final Method INIT_CHANNEL_METHOD;
private final ChannelInitializer<Channel> original;
static {
try {
INIT_CHANNEL_METHOD = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
INIT_CHANNEL_METHOD.setAccessible(true);
} catch (NoSuchMethodException e) {
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private static @Nullable Object paperCompressionEnabledEvent() {
try {
final Class<?> eventClass = Class.forName("io.papermc.paper.network.ConnectionEvent");
return eventClass.getDeclaredField("COMPRESSION_THRESHOLD_SET").get(null);
} catch (final ReflectiveOperationException e) {
return null;
}
}
public BukkitChannelInitializer(ChannelInitializer<Channel> oldInit) {
this.original = oldInit;
}
@@ -62,7 +77,7 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> implem
}
public static void afterChannelInitialize(Channel channel) {
UserConnection connection = new UserConnectionImpl(channel);
final UserConnection connection = new UserConnectionImpl(channel);
new ProtocolPipelineImpl(connection);
if (PaperViaInjector.PAPER_PACKET_LIMITER) {
@@ -70,12 +85,9 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> implem
}
// Add our transformers
HandlerConstructor constructor = ClassGenerator.getConstructor();
MessageToByteEncoder encoder = constructor.newEncodeHandler(connection, (MessageToByteEncoder) channel.pipeline().get("encoder"));
ByteToMessageDecoder decoder = constructor.newDecodeHandler(connection, (ByteToMessageDecoder) channel.pipeline().get("decoder"));
channel.pipeline().replace("encoder", "encoder", encoder);
channel.pipeline().replace("decoder", "decoder", decoder);
final ChannelPipeline pipeline = channel.pipeline();
pipeline.addBefore(MINECRAFT_ENCODER, VIA_ENCODER, new BukkitEncodeHandler(connection));
pipeline.addBefore(MINECRAFT_DECODER, VIA_DECODER, new BukkitDecodeHandler(connection));
}
@Override
@@ -26,59 +26,64 @@ import com.viaversion.viaversion.exception.CancelDecoderException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class BukkitDecodeHandler extends ByteToMessageDecoder {
private final ByteToMessageDecoder minecraftDecoder;
private final UserConnection info;
@ChannelHandler.Sharable
public final class BukkitDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
private final UserConnection connection;
public BukkitDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
this.info = info;
this.minecraftDecoder = minecraftDecoder;
public BukkitDecodeHandler(final UserConnection connection) {
this.connection = connection;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
if (!info.checkServerboundPacket()) {
bytebuf.clear(); // Don't accumulate
protected void decode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List<Object> out) throws Exception {
if (!connection.checkServerboundPacket()) {
throw CancelDecoderException.generate(null);
}
if (!connection.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
}
ByteBuf transformedBuf = null;
final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try {
if (info.shouldTransformPacket()) {
transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
info.transformServerbound(transformedBuf, CancelDecoderException::generate);
}
try {
list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, transformedBuf == null ? bytebuf : transformedBuf));
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
} else if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
}
connection.transformIncoming(transformedBuf, CancelDecoderException::generate);
out.add(transformedBuf.retain());
} finally {
if (transformedBuf != null) {
transformedBuf.release();
}
transformedBuf.release();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return; // ProtocolLib compat
public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) {
return;
}
super.exceptionCaught(ctx, cause);
if (!NMSUtil.isDebugPropertySet() && PipelineUtil.containsCause(cause, InformativeException.class)
&& (info.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
&& (connection.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
cause.printStackTrace(); // Print if CB doesn't already do it
}
}
@Override
public void userEventTriggered(final ChannelHandlerContext ctx, final Object event) throws Exception {
if (BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT == null || event != BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT) {
super.userEventTriggered(ctx, event);
return;
}
// When compression handlers are added, the order becomes Minecraft Encoder -> Compressor -> Via Encoder; fix the order again
final ChannelPipeline pipeline = ctx.pipeline();
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_COMPRESSOR, BukkitChannelInitializer.VIA_ENCODER, pipeline.remove(BukkitChannelInitializer.VIA_ENCODER));
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_DECOMPRESSOR, BukkitChannelInitializer.VIA_DECODER, pipeline.remove(BukkitChannelInitializer.VIA_DECODER));
super.userEventTriggered(ctx, event);
}
}
@@ -24,80 +24,99 @@ import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelEncoderException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.handlers.ChannelHandlerContextWrapper;
import com.viaversion.viaversion.handlers.ViaCodecHandler;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaCodecHandler {
private static Field versionField;
@ChannelHandler.Sharable
public final class BukkitEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
private final UserConnection connection;
private boolean handledCompression = BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT != null;
static {
try {
// Attempt to get any version info from the handler
versionField = NMSUtil.nms(
"PacketEncoder",
"net.minecraft.network.PacketEncoder"
).getDeclaredField("version");
versionField.setAccessible(true);
} catch (Exception e) {
// Not compat version
}
}
private final UserConnection info;
private final MessageToByteEncoder minecraftEncoder;
public BukkitEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
this.info = info;
this.minecraftEncoder = minecraftEncoder;
public BukkitEncodeHandler(final UserConnection connection) {
this.connection = connection;
}
@Override
protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception {
if (versionField != null) {
versionField.set(minecraftEncoder, versionField.get(this));
protected void encode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List<Object> out) throws Exception {
if (!connection.checkClientboundPacket()) {
throw CancelEncoderException.generate(null);
}
// handle the packet type
if (!(o instanceof ByteBuf)) {
// call minecraft encoder
try {
PipelineUtil.callEncode(this.minecraftEncoder, new ChannelHandlerContextWrapper(ctx, this), o, bytebuf);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
} else if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
if (!connection.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
}
final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try {
final boolean needsCompression = !handledCompression && handleCompressionOrder(ctx, transformedBuf);
connection.transformClientbound(transformedBuf, CancelEncoderException::generate);
if (needsCompression) {
recompress(ctx, transformedBuf);
}
} else {
bytebuf.writeBytes((ByteBuf) o);
out.add(transformedBuf.retain());
} finally {
transformedBuf.release();
}
}
private boolean handleCompressionOrder(final ChannelHandlerContext ctx, final ByteBuf buf) throws Exception {
final ChannelPipeline pipeline = ctx.pipeline();
final List<String> names = pipeline.names();
final int compressorIndex = names.indexOf(BukkitChannelInitializer.MINECRAFT_COMPRESSOR);
if (compressorIndex == -1) {
return false;
}
handledCompression = true;
if (compressorIndex > names.indexOf(BukkitChannelInitializer.VIA_ENCODER)) {
// Need to decompress this packet due to bad order
final ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((ByteToMessageDecoder) pipeline.get(BukkitChannelInitializer.MINECRAFT_DECOMPRESSOR), ctx, buf).get(0);
try {
buf.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_COMPRESSOR, BukkitChannelInitializer.VIA_ENCODER, pipeline.remove(BukkitChannelInitializer.VIA_ENCODER));
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_DECOMPRESSOR, BukkitChannelInitializer.VIA_DECODER, pipeline.remove(BukkitChannelInitializer.VIA_DECODER));
return true;
}
return false;
}
private void recompress(final ChannelHandlerContext ctx, final ByteBuf buf) throws Exception {
final ByteBuf compressed = ctx.alloc().buffer();
try {
PipelineUtil.callEncode((MessageToByteEncoder<ByteBuf>) ctx.pipeline().get(BukkitChannelInitializer.MINECRAFT_COMPRESSOR), ctx, buf, compressed);
buf.clear().writeBytes(compressed);
} finally {
compressed.release();
}
transform(bytebuf);
}
@Override
public void transform(ByteBuf bytebuf) throws Exception {
if (!info.checkClientboundPacket()) throw CancelEncoderException.generate(null);
if (!info.shouldTransformPacket()) return;
info.transformClientbound(bytebuf, CancelEncoderException::generate);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return; // ProtocolLib compat
public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) {
return;
}
super.exceptionCaught(ctx, cause);
if (!NMSUtil.isDebugPropertySet() && PipelineUtil.containsCause(cause, InformativeException.class)
&& (info.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
&& (connection.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
cause.printStackTrace(); // Print if CB doesn't already do it
}
}
public UserConnection connection() {
return connection;
}
}
@@ -0,0 +1,116 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.listeners;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import io.netty.channel.Channel;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.logging.Level;
public class JoinListener implements Listener {
private static final Method GET_HANDLE;
private static final Field CONNECTION;
private static final Field NETWORK_MANAGER;
private static final Field CHANNEL;
static {
Method gh = null;
Field conn = null, nm = null, ch = null;
try {
gh = NMSUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle");
conn = findField(gh.getReturnType(), "PlayerConnection", "ServerGamePacketListenerImpl");
nm = findField(conn.getType(), "NetworkManager", "Connection");
ch = findField(nm.getType(), "Channel");
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) {
Via.getPlatform().getLogger().log(
Level.WARNING,
"Couldn't find reflection methods/fields to access Channel from player.\n" +
"Login race condition fixer will be disabled.\n" +
" Some plugins that use ViaAPI on join event may work incorrectly.", e);
}
GET_HANDLE = gh;
CONNECTION = conn;
NETWORK_MANAGER = nm;
CHANNEL = ch;
}
// Loosely search a field with any name, as long as it matches a type name.
private static Field findField(Class<?> cl, String... types) throws NoSuchFieldException {
for (Field field : cl.getDeclaredFields()) {
for (String type : types)
if (field.getType().getSimpleName().equals(type))
return field;
}
throw new NoSuchFieldException(types[0]);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent e) {
if (CHANNEL == null) return;
Player player = e.getPlayer();
Channel channel;
try {
channel = getChannel(player);
} catch (Exception ex) {
Via.getPlatform().getLogger().log(Level.WARNING, ex,
() -> "Could not find Channel for logging-in player " + player.getUniqueId());
return;
}
// The connection has already closed, that was a quick leave
if (!channel.isOpen()) return;
UserConnection user = getUserConnection(channel);
if (user == null) {
Via.getPlatform().getLogger().log(Level.WARNING,
"Could not find UserConnection for logging-in player {0}",
player.getUniqueId());
return;
}
ProtocolInfo info = user.getProtocolInfo();
info.setUuid(player.getUniqueId());
info.setUsername(player.getName());
Via.getManager().getConnectionManager().onLoginSuccess(user);
}
private @Nullable UserConnection getUserConnection(Channel channel) {
BukkitEncodeHandler encoder = channel.pipeline().get(BukkitEncodeHandler.class);
return encoder != null ? encoder.connection() : null;
}
private Channel getChannel(Player player) throws Exception {
Object entityPlayer = GET_HANDLE.invoke(player);
Object pc = CONNECTION.get(entityPlayer);
Object nm = NETWORK_MANAGER.get(pc);
return (Channel) CHANNEL.get(nm);
}
}
@@ -1,62 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.listeners;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.bukkit.platform.BukkitViaInjector;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable;
public class ProtocolLibEnableListener implements Listener {
@EventHandler
public void onPluginEnable(PluginEnableEvent e) {
// Will likely never happen, but try to account for hacky plugin loading systems anyways
if (e.getPlugin().getName().equals("ProtocolLib")) {
checkCompat(e.getPlugin());
}
}
@EventHandler
public void onPluginDisable(PluginDisableEvent e) {
if (e.getPlugin().getName().equals("ProtocolLib")) {
((BukkitViaInjector) Via.getManager().getInjector()).setProtocolLib(false);
}
}
public static void checkCompat(@Nullable Plugin protocolLib) {
if (protocolLib != null) {
String version = protocolLib.getDescription().getVersion();
String majorVersion = version.split("\\.", 2)[0];
try {
// Only need the compat check for version < 5
if (Integer.parseInt(majorVersion) < 5) {
((BukkitViaInjector) Via.getManager().getInjector()).setProtocolLib(true);
return;
}
} catch (NumberFormatException ignored) {
Via.getPlatform().getLogger().warning("ProtocolLib version check failed for version " + version);
}
}
((BukkitViaInjector) Via.getManager().getInjector()).setProtocolLib(false);
}
}
@@ -0,0 +1,70 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.listeners.protocol1_19to1_18_2;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.Protocol1_19To1_18_2;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent;
public final class BlockBreakListener extends ViaBukkitListener {
private static final Class<?> CRAFT_BLOCK_STATE_CLASS;
static {
try {
CRAFT_BLOCK_STATE_CLASS = NMSUtil.obc("block.CraftBlockState");
} catch (final ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public BlockBreakListener(ViaVersionPlugin plugin) {
super(plugin, Protocol1_19To1_18_2.class);
}
@EventHandler(priority = EventPriority.MONITOR)
public void blockBreak(BlockBreakEvent event) {
final Block block = event.getBlock();
if (!event.isCancelled() || !isBlockEntity(block.getState())) {
return;
}
// We need to resend the block entity data after an ack has been sent out
final int serverProtocolVersion = Via.getAPI().getServerVersion().highestSupportedVersion();
final long delay = serverProtocolVersion > ProtocolVersion.v1_8.getVersion() && serverProtocolVersion < ProtocolVersion.v1_14.getVersion() ? 2 : 1;
getPlugin().getServer().getScheduler().runTaskLater(getPlugin(), () -> {
final BlockState state = block.getState();
if (isBlockEntity(state)) {
state.update(true, false);
}
}, delay);
}
private boolean isBlockEntity(final BlockState state) {
// We love legacy versions
return state.getClass() != CRAFT_BLOCK_STATE_CLASS;
}
}
@@ -43,14 +43,17 @@ public class BukkitViaAPI extends ViaAPIBase<Player> {
@Override
public int getPlayerVersion(UUID uuid) {
UserConnection connection = Via.getManager().getConnectionManager().getConnectedClient(uuid);
if (connection == null) {
if (connection != null) {
return connection.getProtocolInfo().getProtocolVersion();
}
if (isProtocolSupport()) {
Player player = Bukkit.getPlayer(uuid);
if (player != null && isProtocolSupport()) {
if (player != null) {
return ProtocolSupportUtil.getProtocolVersion(player);
}
return -1;
}
return connection.getProtocolInfo().getProtocolVersion();
return -1;
}
@Override
@@ -58,16 +61,6 @@ public class BukkitViaAPI extends ViaAPIBase<Player> {
sendRawPacket(player.getUniqueId(), packet);
}
/**
* Returns if this version is a compatibility build for spigot.
* Eg. 1.9.1 / 1.9.2 allow certain versions to connect
*
* @return true if compat Spigot build
*/
public boolean isCompatSpigotBuild() {
return plugin.isCompatSpigotBuild();
}
/**
* Returns if ProtocolSupport is also being used.
*
@@ -76,5 +69,4 @@ public class BukkitViaAPI extends ViaAPIBase<Player> {
public boolean isProtocolSupport() {
return plugin.isProtocolSupport();
}
}
@@ -35,7 +35,6 @@ import java.lang.reflect.Method;
import java.util.List;
public class BukkitViaInjector extends LegacyViaInjector {
private boolean protocolLib;
@Override
public void inject() throws ReflectiveOperationException {
@@ -112,11 +111,6 @@ public class BukkitViaInjector extends LegacyViaInjector {
throw new RuntimeException("Failed to get server");
}
@Override
public String getDecoderName() {
return protocolLib ? "protocol_lib_decoder" : "decoder";
}
@Override
protected @Nullable Object getServerConnection() throws ReflectiveOperationException {
Class<?> serverClass = NMSUtil.nms(
@@ -189,8 +183,4 @@ public class BukkitViaInjector extends LegacyViaInjector {
}
return false;
}
public void setProtocolLib(boolean protocolLib) {
this.protocolLib = protocolLib;
}
}
@@ -23,10 +23,12 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.platform.ViaPlatformLoader;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
import com.viaversion.viaversion.bukkit.compat.ProtocolSupportCompat;
import com.viaversion.viaversion.bukkit.listeners.JoinListener;
import com.viaversion.viaversion.bukkit.listeners.UpdateListener;
import com.viaversion.viaversion.bukkit.listeners.multiversion.PlayerSneakListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_15to1_14_4.EntityToggleGlideListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_19to1_18_2.BlockBreakListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_9to1_8.ArmorListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_9to1_8.BlockListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_9to1_8.DeathListener;
@@ -79,11 +81,17 @@ public class BukkitViaLoader implements ViaPlatformLoader {
// Update Listener
registerListener(new UpdateListener());
// Login listener
registerListener(new JoinListener());
/* Base Protocol */
final ViaVersionPlugin plugin = (ViaVersionPlugin) Bukkit.getPluginManager().getPlugin("ViaVersion");
// Add ProtocolSupport ConnectListener if necessary.
ClassGenerator.registerPSConnectListener(plugin);
if (plugin.isProtocolSupport() && ProtocolSupportCompat.isMultiplatformPS()) {
ProtocolSupportCompat.registerPSConnectListener(plugin);
}
int serverProtocolVersion = Via.getAPI().getServerVersion().lowestSupportedVersion();
/* 1.9 client to 1.8 server */
@@ -177,6 +185,7 @@ public class BukkitViaLoader implements ViaPlatformLoader {
}
if (serverProtocolVersion < ProtocolVersion.v1_19.getVersion()) {
Via.getManager().getProviders().use(AckSequenceProvider.class, new BukkitAckSequenceProvider(plugin));
storeListener(new BlockBreakListener(plugin)).register();
}
}
@@ -69,7 +69,7 @@ public final class PaperViaInjector {
}
private static boolean hasPacketLimiter() {
return hasClass("com.destroystokyo.paper.PaperConfig$PacketLimit") || hasClass("io.papermc.paper.PaperConfig$PacketLimit");
return hasClass("com.destroystokyo.paper.PaperConfig$PacketLimit") || hasClass("io.papermc.paper.configuration.GlobalConfiguration$PacketLimiter");
}
private static boolean hasClass(final String className) {
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.bukkit.providers;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.bukkit.tasks.protocol1_19to1_18_2.AckSequenceTask;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.provider.AckSequenceProvider;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.storage.SequenceStorage;
@@ -36,7 +37,9 @@ public final class BukkitAckSequenceProvider extends AckSequenceProvider {
final SequenceStorage sequenceStorage = connection.get(SequenceStorage.class);
final int previousSequence = sequenceStorage.setSequenceId(sequence);
if (previousSequence == -1) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new AckSequenceTask(connection, sequenceStorage), 1);
final int serverProtocolVersion = connection.getProtocolInfo().getServerProtocolVersion();
final long delay = serverProtocolVersion > ProtocolVersion.v1_8.getVersion() && serverProtocolVersion < ProtocolVersion.v1_14.getVersion() ? 2 : 1;
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new AckSequenceTask(connection, sequenceStorage), delay);
}
}
}
@@ -19,7 +19,7 @@ package com.viaversion.viaversion.bukkit.util;
import org.bukkit.Bukkit;
public class NMSUtil {
public final class NMSUtil {
private static final String BASE = Bukkit.getServer().getClass().getPackage().getName();
private static final String NMS = BASE.replace("org.bukkit.craftbukkit", "net.minecraft.server");
private static final boolean DEBUG_PROPERTY = loadDebugProperty();
@@ -53,10 +53,6 @@ public class NMSUtil {
return Class.forName(BASE + "." + className);
}
public static String getVersion() {
return BASE.substring(BASE.lastIndexOf('.') + 1);
}
/**
* @return true if debug=true is set in the server.properties (added by CB)
*/
@@ -22,27 +22,31 @@ import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ProtocolSupportUtil {
private static Method protocolVersionMethod = null;
private static Method getIdMethod = null;
public final class ProtocolSupportUtil {
private static final Method PROTOCOL_VERSION_METHOD;
private static final Method GET_ID_METHOD;
static {
Method protocolVersionMethod = null;
Method getIdMethod = null;
try {
protocolVersionMethod = Class.forName("protocolsupport.api.ProtocolSupportAPI").getMethod("getProtocolVersion", Player.class);
getIdMethod = Class.forName("protocolsupport.api.ProtocolVersion").getMethod("getId");
} catch (Exception e) {
} catch (ReflectiveOperationException e) {
// ProtocolSupport not installed.
}
PROTOCOL_VERSION_METHOD = protocolVersionMethod;
GET_ID_METHOD = getIdMethod;
}
public static int getProtocolVersion(Player player) {
if (protocolVersionMethod == null) return -1;
if (PROTOCOL_VERSION_METHOD == null) {
return -1;
}
try {
Object version = protocolVersionMethod.invoke(null, player);
return (int) getIdMethod.invoke(version);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
Object version = PROTOCOL_VERSION_METHOD.invoke(null, player);
return (int) GET_ID_METHOD.invoke(version);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return -1;
@@ -20,17 +20,25 @@ package com.viaversion.viaversion.classgenerator.generated;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.bukkit.handlers.BukkitDecodeHandler;
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
public class BasicHandlerConstructor implements HandlerConstructor {
@Override
public BukkitEncodeHandler newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
return new BukkitEncodeHandler(info, minecraftEncoder);
}
public interface HandlerSupplier {
@Override
public BukkitDecodeHandler newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
return new BukkitDecodeHandler(info, minecraftDecoder);
MessageToMessageEncoder<ByteBuf> newEncodeHandler(UserConnection connection);
MessageToMessageDecoder<ByteBuf> newDecodeHandler(UserConnection connection);
final class DefaultHandlerSupplier implements HandlerSupplier {
@Override
public MessageToMessageEncoder<ByteBuf> newEncodeHandler(final UserConnection connection) {
return new BukkitEncodeHandler(connection);
}
@Override
public MessageToMessageDecoder<ByteBuf> newDecodeHandler(final UserConnection connection) {
return new BukkitDecodeHandler(connection);
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
dependencies {
implementation(projects.viaversionCommon)
implementation(projects.javaCompat)
implementation(projects.compat)
compileOnly(libs.bungee)
}
@@ -17,6 +17,7 @@
*/
package com.viaversion.viaversion;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.ViaAPI;
@@ -24,17 +25,19 @@ import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.configuration.ConfigurationProvider;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.platform.PlatformTask;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.platform.UnsupportedSoftware;
import com.viaversion.viaversion.api.platform.ViaServerProxyPlatform;
import com.viaversion.viaversion.bungee.commands.BungeeCommand;
import com.viaversion.viaversion.bungee.commands.BungeeCommandHandler;
import com.viaversion.viaversion.bungee.commands.BungeeCommandSender;
import com.viaversion.viaversion.bungee.platform.BungeeViaTask;
import com.viaversion.viaversion.bungee.platform.BungeeViaAPI;
import com.viaversion.viaversion.bungee.platform.BungeeViaConfig;
import com.viaversion.viaversion.bungee.platform.BungeeViaInjector;
import com.viaversion.viaversion.bungee.platform.BungeeViaLoader;
import com.viaversion.viaversion.bungee.platform.BungeeViaTask;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.dump.PluginInfo;
import com.viaversion.viaversion.unsupported.UnsupportedServerSoftware;
import com.viaversion.viaversion.util.GsonUtil;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -49,14 +52,15 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BungeePlugin extends Plugin implements ViaPlatform<ProxiedPlayer>, Listener {
public class BungeePlugin extends Plugin implements ViaServerProxyPlatform<ProxiedPlayer>, Listener {
private final ProtocolDetectorService protocolDetectorService = new ProtocolDetectorService();
private BungeeViaAPI api;
private BungeeViaConfig config;
@Override
public void onLoad() {
try {
ProtocolConstants.class.getField("MINECRAFT_1_19");
ProtocolConstants.class.getField("MINECRAFT_1_19_1");
} catch (NoSuchFieldException e) {
getLogger().warning(" / \\");
getLogger().warning(" / \\");
@@ -197,7 +201,7 @@ public class BungeePlugin extends Plugin implements ViaPlatform<ProxiedPlayer>,
));
platformSpecific.add("plugins", GsonUtil.getGson().toJsonTree(plugins));
platformSpecific.add("servers", GsonUtil.getGson().toJsonTree(ProtocolDetectorService.getDetectedIds()));
platformSpecific.add("servers", GsonUtil.getGson().toJsonTree(protocolDetectorService.detectedProtocolVersions()));
return platformSpecific;
}
@@ -206,8 +210,24 @@ public class BungeePlugin extends Plugin implements ViaPlatform<ProxiedPlayer>,
return true;
}
@Override
public Collection<UnsupportedSoftware> getUnsupportedSoftwareClasses() {
final Collection<UnsupportedSoftware> list = new ArrayList<>(ViaServerProxyPlatform.super.getUnsupportedSoftwareClasses());
list.add(new UnsupportedServerSoftware.Builder()
.name("FlameCord")
.addClassName("dev._2lstudios.flamecord.FlameCord")
.reason(UnsupportedServerSoftware.Reason.BREAKING_PROXY_SOFTWARE)
.build());
return ImmutableList.copyOf(list);
}
@Override
public boolean hasPlugin(final String name) {
return getProxy().getPluginManager().getPlugin(name) != null;
}
@Override
public ProtocolDetectorService protocolDetectorService() {
return protocolDetectorService;
}
}
@@ -21,7 +21,6 @@ import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.command.ViaSubCommand;
import com.viaversion.viaversion.bungee.platform.BungeeViaConfig;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
public class ProbeSubCmd extends ViaSubCommand {
@Override
@@ -38,7 +37,7 @@ public class ProbeSubCmd extends ViaSubCommand {
@Override
public boolean execute(ViaCommandSender sender, String[] args) {
ProtocolDetectorService.getInstance().run();
Via.proxyPlatform().protocolDetectorService().probeAllServers();
sendMessage(sender, "&6Started searching for protocol versions");
return true;
}
@@ -29,7 +29,6 @@ import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.bungee.storage.BungeeStorage;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ClientboundPackets1_9;
@@ -96,7 +95,7 @@ public class BungeeServerHandler implements Listener {
user.put(new BungeeStorage(e.getPlayer()));
}
int protocolId = ProtocolDetectorService.getProtocolId(e.getTarget().getName());
int protocolId = Via.proxyPlatform().protocolDetectorService().serverProtocolVersion(e.getTarget().getName());
List<ProtocolPathEntry> protocols = Via.getManager().getProtocolManager().getProtocolPath(user.getProtocolInfo().getProtocolVersion(), protocolId);
// Check if ViaVersion can support that version
@@ -164,7 +163,7 @@ public class BungeeServerHandler implements Listener {
storage.setCurrentServer(serverName);
int protocolId = ProtocolDetectorService.getProtocolId(serverName);
int protocolId = Via.proxyPlatform().protocolDetectorService().serverProtocolVersion(serverName);
if (protocolId <= ProtocolVersion.v1_8.getVersion()) { // 1.8 doesn't have BossBar packet
if (storage.getBossbar() != null) {
@@ -18,6 +18,7 @@
package com.viaversion.viaversion.bungee.platform;
import com.viaversion.viaversion.ViaAPIBase;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import io.netty.buffer.ByteBuf;
import net.md_5.bungee.api.config.ServerInfo;
@@ -41,6 +42,6 @@ public class BungeeViaAPI extends ViaAPIBase<ProxiedPlayer> {
* @param serverInfo The serverinfo to probe
*/
public void probeServer(ServerInfo serverInfo) {
ProtocolDetectorService.probeServer(serverInfo);
((ProtocolDetectorService) Via.proxyPlatform().protocolDetectorService()).probeServer(serverInfo);
}
}
@@ -30,7 +30,6 @@ import com.viaversion.viaversion.bungee.providers.BungeeEntityIdProvider;
import com.viaversion.viaversion.bungee.providers.BungeeMainHandProvider;
import com.viaversion.viaversion.bungee.providers.BungeeMovementTransmitter;
import com.viaversion.viaversion.bungee.providers.BungeeVersionProvider;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.BossBarProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.EntityIdProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MainHandProvider;
@@ -44,10 +43,9 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
public class BungeeViaLoader implements ViaPlatformLoader {
private final BungeePlugin plugin;
private final Set<Listener> listeners = new HashSet<>();
private final Set<ScheduledTask> tasks = new HashSet<>();
private final BungeePlugin plugin;
public BungeeViaLoader(BungeePlugin plugin) {
this.plugin = plugin;
@@ -82,7 +80,7 @@ public class BungeeViaLoader implements ViaPlatformLoader {
if (plugin.getConf().getBungeePingInterval() > 0) {
tasks.add(plugin.getProxy().getScheduler().schedule(
plugin,
new ProtocolDetectorService(plugin),
() -> Via.proxyPlatform().protocolDetectorService().probeAllServers(),
0, plugin.getConf().getBungeePingInterval(),
TimeUnit.SECONDS
));
@@ -17,93 +17,77 @@
*/
package com.viaversion.viaversion.bungee.service;
import com.viaversion.viaversion.BungeePlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.bungee.platform.BungeeViaConfig;
import com.viaversion.viaversion.bungee.providers.BungeeVersionProvider;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ServerPing;
import com.viaversion.viaversion.platform.AbstractProtocolDetectorService;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import java.util.HashMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;
public class ProtocolDetectorService implements Runnable {
private static final Map<String, Integer> detectedProtocolIds = new ConcurrentHashMap<>();
private static ProtocolDetectorService instance;
private final BungeePlugin plugin;
public final class ProtocolDetectorService extends AbstractProtocolDetectorService {
public ProtocolDetectorService(BungeePlugin plugin) {
this.plugin = plugin;
instance = this;
}
public void probeServer(final ServerInfo serverInfo) {
final String serverName = serverInfo.getName();
serverInfo.ping((serverPing, throwable) -> {
// Ensure protocol is positive, some services will return -1
if (throwable != null || serverPing == null || serverPing.getVersion() == null || serverPing.getVersion().getProtocol() <= 0) {
return;
}
public static Integer getProtocolId(String serverName) {
// Step 1. Check Config
Map<String, Integer> servers = ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
Integer protocol = servers.get(serverName);
if (protocol != null) {
return protocol;
}
// Step 2. Check Detected
Integer detectedProtocol = detectedProtocolIds.get(serverName);
if (detectedProtocol != null) {
return detectedProtocol;
}
// Step 3. Use Default
Integer defaultProtocol = servers.get("default");
if (defaultProtocol != null) {
return defaultProtocol;
}
// Step 4: Use bungee lowest supported... *cries*
return BungeeVersionProvider.getLowestSupportedVersion();
}
final int oldProtocolVersion = serverProtocolVersion(serverName);
if (oldProtocolVersion == serverPing.getVersion().getProtocol()) {
// Same value as previously
return;
}
@Override
public void run() {
for (final Map.Entry<String, ServerInfo> lists : plugin.getProxy().getServers().entrySet()) {
probeServer(lists.getValue());
}
}
setProtocolVersion(serverName, serverPing.getVersion().getProtocol());
public static void probeServer(final ServerInfo serverInfo) {
final String key = serverInfo.getName();
serverInfo.ping(new Callback<ServerPing>() {
@Override
public void done(ServerPing serverPing, Throwable throwable) {
if (throwable == null && serverPing != null && serverPing.getVersion() != null) {
// Ensure protocol is positive, some services will return -1
if (serverPing.getVersion().getProtocol() > 0) {
detectedProtocolIds.put(key, serverPing.getVersion().getProtocol());
if (((BungeeViaConfig) Via.getConfig()).isBungeePingSave()) {
Map<String, Integer> servers = ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
Integer protocol = servers.get(key);
if (protocol != null && protocol == serverPing.getVersion().getProtocol()) {
return;
}
// Ensure we're the only ones writing to the config
synchronized (Via.getPlatform().getConfigurationProvider()) {
servers.put(key, serverPing.getVersion().getProtocol());
}
// Save
Via.getPlatform().getConfigurationProvider().saveConfig();
}
}
if (((BungeeViaConfig) Via.getConfig()).isBungeePingSave()) {
final Map<String, Integer> servers = ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
final Integer protocol = servers.get(serverName);
if (protocol != null && protocol == serverPing.getVersion().getProtocol()) {
return;
}
// Ensure we're the only ones writing to the config
synchronized (Via.getPlatform().getConfigurationProvider()) {
servers.put(serverName, serverPing.getVersion().getProtocol());
}
Via.getPlatform().getConfigurationProvider().saveConfig();
}
});
}
public static Map<String, Integer> getDetectedIds() {
return new HashMap<>(detectedProtocolIds);
@Override
public void probeAllServers() {
final Collection<ServerInfo> servers = ProxyServer.getInstance().getServers().values();
final Set<String> serverNames = new HashSet<>(servers.size());
for (final ServerInfo serverInfo : servers) {
probeServer(serverInfo);
serverNames.add(serverInfo.getName());
}
// Remove servers that aren't registered anymore
lock.writeLock().lock();
try {
detectedProtocolIds.keySet().retainAll(serverNames);
} finally {
lock.writeLock().unlock();
}
}
public static ProtocolDetectorService getInstance() {
return instance;
@Override
protected Map<String, Integer> configuredServers() {
return ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
}
public BungeePlugin getPlugin() {
return plugin;
@Override
protected int lowestSupportedProtocolVersion() {
return BungeeVersionProvider.getLowestSupportedVersion();
}
}
@@ -157,7 +157,7 @@ public class ViaManagerImpl implements ViaManager {
loader.load();
// Common tasks
mappingLoadingTask = Via.getPlatform().runRepeatingSync(() -> {
if (protocolManager.checkForMappingCompletion()) {
if (protocolManager.checkForMappingCompletion() && mappingLoadingTask != null) {
mappingLoadingTask.cancel();
mappingLoadingTask = null;
}
@@ -237,7 +237,7 @@ public class ViaManagerImpl implements ViaManager {
private void unsupportedSoftwareWarning() {
boolean found = false;
for (UnsupportedSoftware software : platform.getUnsupportedSoftwareClasses()) {
for (final UnsupportedSoftware software : platform.getUnsupportedSoftwareClasses()) {
if (!software.findMatch()) {
continue;
}
@@ -20,6 +20,7 @@ package com.viaversion.viaversion.connection;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ConnectionManager;
import com.viaversion.viaversion.api.connection.UserConnection;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -37,17 +38,29 @@ public class ConnectionManagerImpl implements ConnectionManager {
@Override
public void onLoginSuccess(UserConnection connection) {
Objects.requireNonNull(connection, "connection is null!");
connections.add(connection);
Channel channel = connection.getChannel();
// This user has already disconnected...
if (channel != null && !channel.isOpen()) return;
boolean newlyAdded = connections.add(connection);
if (isFrontEnd(connection)) {
UUID id = connection.getProtocolInfo().getUuid();
if (clients.put(id, connection) != null) {
UserConnection previous = clients.put(id, connection);
if (previous != null && previous != connection) {
Via.getPlatform().getLogger().warning("Duplicate UUID on frontend connection! (" + id + ")");
}
}
if (connection.getChannel() != null) {
connection.getChannel().closeFuture().addListener((ChannelFutureListener) future -> onDisconnect(connection));
if (channel != null) {
// We managed to add a user that had already disconnected!
// Let's clean up the mess here and now
if (!channel.isOpen()) {
onDisconnect(connection);
} else if (newlyAdded) { // Setup to clean-up on disconnect
channel.closeFuture().addListener((ChannelFutureListener) future -> onDisconnect(connection));
}
}
}
@@ -0,0 +1,100 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.platform;
import com.viaversion.viaversion.api.platform.ProtocolDetectorService;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class AbstractProtocolDetectorService implements ProtocolDetectorService {
protected final Object2IntMap<String> detectedProtocolIds = new Object2IntOpenHashMap<>();
protected final ReadWriteLock lock = new ReentrantReadWriteLock();
protected AbstractProtocolDetectorService() {
detectedProtocolIds.defaultReturnValue(-1);
}
@Override
public int serverProtocolVersion(final String serverName) {
// Step 1. Check detected
lock.readLock().lock();
final int detectedProtocol;
try {
detectedProtocol = detectedProtocolIds.getInt(serverName);
} finally {
lock.readLock().unlock();
}
if (detectedProtocol != -1) {
return detectedProtocol;
}
// Step 2. Check config (CME moment?)
final Map<String, Integer> servers = configuredServers();
final Integer protocol = servers.get(serverName);
if (protocol != null) {
return protocol;
}
// Step 3. Use Default (CME moment intensifies?)
final Integer defaultProtocol = servers.get("default");
if (defaultProtocol != null) {
return defaultProtocol;
}
// Step 4: Use the proxy's lowest supported... *cries*
return lowestSupportedProtocolVersion();
}
@Override
public void setProtocolVersion(final String serverName, final int protocolVersion) {
lock.writeLock().lock();
try {
detectedProtocolIds.put(serverName, protocolVersion);
} finally {
lock.writeLock().unlock();
}
}
@Override
public int uncacheProtocolVersion(final String serverName) {
lock.writeLock().lock();
try {
return detectedProtocolIds.removeInt(serverName);
} finally {
lock.writeLock().unlock();
}
}
@Override
public Object2IntMap<String> detectedProtocolVersions() {
lock.readLock().lock();
try {
return new Object2IntOpenHashMap<>(detectedProtocolIds);
} finally {
lock.readLock().unlock();
}
}
protected abstract Map<String, Integer> configuredServers();
protected abstract int lowestSupportedProtocolVersion();
}
@@ -235,16 +235,6 @@ public abstract class LegacyViaInjector implements ViaInjector {
return data;
}
@Override
public String getEncoderName() {
return "encoder";
}
@Override
public String getDecoderName() {
return "decoder";
}
/**
* Returns the Vanilla server connection object the channels to be injected should be searched in.
*
@@ -68,6 +68,7 @@ import com.viaversion.viaversion.protocols.protocol1_18_2to1_18.Protocol1_18_2To
import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.Protocol1_18To1_17_1;
import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.Protocol1_19_1To1_19;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.Protocol1_19To1_18_2;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.Protocol1_19_3To1_19_1;
import com.viaversion.viaversion.protocols.protocol1_9_1_2to1_9_3_4.Protocol1_9_1_2To1_9_3_4;
import com.viaversion.viaversion.protocols.protocol1_9_1to1_9.Protocol1_9_1To1_9;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.Protocol1_9_3To1_9_1_2;
@@ -176,6 +177,8 @@ public class ProtocolManagerImpl implements ProtocolManager {
registerProtocol(new Protocol1_19To1_18_2(), ProtocolVersion.v1_19, ProtocolVersion.v1_18_2);
registerProtocol(new Protocol1_19_1To1_19(), ProtocolVersion.v1_19_1, ProtocolVersion.v1_19);
registerProtocol(new Protocol1_19_3To1_19_1(), ProtocolVersion.v1_19_3, ProtocolVersion.v1_19_1);
}
@Override
@@ -25,12 +25,15 @@ import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_12Types;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.rewriter.ItemRewriter;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.version.Types1_12;
@@ -38,6 +41,8 @@ import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.protocol1_12to1_11_1.metadata.MetadataRewriter1_12To1_11_1;
import com.viaversion.viaversion.protocols.protocol1_12to1_11_1.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_12to1_11_1.providers.InventoryQuickMoveProvider;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.ClientboundPackets1_13;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viaversion.protocols.protocol1_9_1_2to1_9_3_4.types.Chunk1_9_3_4Type;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.ClientboundPackets1_9_3;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.ServerboundPackets1_9_3;
@@ -125,33 +130,28 @@ public class Protocol1_12To1_11_1 extends AbstractProtocol<ClientboundPackets1_9
Chunk1_9_3_4Type type = new Chunk1_9_3_4Type(clientWorld);
Chunk chunk = wrapper.passthrough(type);
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null)
continue;
for (int s = 0; s < chunk.getSections().length; s++) {
ChunkSection section = chunk.getSections()[s];
if (section == null) continue;
DataPalette blocks = section.palette(PaletteType.BLOCKS);
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getBlockWithoutData(x, y, z);
// Is this a bed?
if (block == 26) {
// NBT -> { color:14, x:132, y:64, z:222, id:"minecraft:bed" } (Debug output)
CompoundTag tag = new CompoundTag();
tag.put("color", new IntTag(14)); // Set color to red (Default in previous versions)
tag.put("x", new IntTag(x + (chunk.getX() << 4)));
tag.put("y", new IntTag(y + (i << 4)));
tag.put("z", new IntTag(z + (chunk.getZ() << 4)));
tag.put("id", new StringTag("minecraft:bed"));
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx) >> 4;
// Is this a bed?
if (id != 26) continue;
// Add a fake block entity
chunk.getBlockEntities().add(tag);
}
}
}
// NBT -> { color:14, x:132, y:64, z:222, id:"minecraft:bed" } (Debug output)
CompoundTag tag = new CompoundTag();
tag.put("color", new IntTag(14)); // Set color to red (Default in previous versions)
tag.put("x", new IntTag(ChunkSection.xFromIndex(idx) + (chunk.getX() << 4)));
tag.put("y", new IntTag(ChunkSection.yFromIndex(idx) + (s << 4)));
tag.put("z", new IntTag(ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4)));
tag.put("id", new StringTag("minecraft:bed"));
// Add a fake block entity
chunk.getBlockEntities().add(tag);
}
}
}
});
}
@@ -167,9 +167,16 @@ public class Protocol1_12To1_11_1 extends AbstractProtocol<ClientboundPackets1_9
map(Type.UNSIGNED_BYTE);
map(Type.INT);
handler(wrapper -> {
ClientWorld clientChunks = wrapper.user().get(ClientWorld.class);
UserConnection user = wrapper.user();
ClientWorld clientChunks = user.get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 1);
clientChunks.setEnvironment(dimensionId);
// Reset recipes
if (user.getProtocolInfo().getProtocolVersion() >= ProtocolVersion.v1_13.getVersion()) {
wrapper.create(ClientboundPackets1_13.DECLARE_RECIPES, packetWrapper -> packetWrapper.write(Type.VAR_INT, 0))
.scheduleSend(Protocol1_13To1_12_2.class);
}
});
}
});
@@ -28,6 +28,8 @@ import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.ClientboundPackets1_13;
@@ -75,31 +77,25 @@ public class ConnectionData {
}
public static void updateChunkSectionNeighbours(UserConnection user, int chunkX, int chunkZ, int chunkSectionY) {
int chunkMinY = chunkSectionY << 4;
List<BlockChangeRecord1_8> updates = new ArrayList<>();
for (int chunkDeltaX = -1; chunkDeltaX <= 1; chunkDeltaX++) {
for (int chunkDeltaZ = -1; chunkDeltaZ <= 1; chunkDeltaZ++) {
if (Math.abs(chunkDeltaX) + Math.abs(chunkDeltaZ) == 0) continue;
int distance = Math.abs(chunkDeltaX) + Math.abs(chunkDeltaZ);
if (distance == 0) continue;
List<BlockChangeRecord1_8> updates = new ArrayList<>();
if (Math.abs(chunkDeltaX) + Math.abs(chunkDeltaZ) == 2) { // Corner
for (int blockY = chunkSectionY * 16; blockY < chunkSectionY * 16 + 16; blockY++) {
int chunkMinX = (chunkX + chunkDeltaX) << 4;
int chunkMinZ = (chunkZ + chunkDeltaZ) << 4;
if (distance == 2) { // Corner
for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) {
int blockPosX = chunkDeltaX == 1 ? 0 : 15;
int blockPosZ = chunkDeltaZ == 1 ? 0 : 15;
updateBlock(user,
new Position(
((chunkX + chunkDeltaX) << 4) + blockPosX,
(short) blockY,
((chunkZ + chunkDeltaZ) << 4) + blockPosZ
),
updates
);
updateBlock(user, new Position(chunkMinX + blockPosX, blockY, chunkMinZ + blockPosZ), updates);
}
} else {
for (int blockY = chunkSectionY * 16; blockY < chunkSectionY * 16 + 16; blockY++) {
int xStart;
int xEnd;
int zStart;
int zEnd;
for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) {
int xStart, xEnd;
int zStart, zEnd;
if (chunkDeltaX == 1) {
xStart = 0;
xEnd = 2;
@@ -123,13 +119,7 @@ public class ConnectionData {
}
for (int blockX = xStart; blockX < xEnd; blockX++) {
for (int blockZ = zStart; blockZ < zEnd; blockZ++) {
updateBlock(user,
new Position(
((chunkX + chunkDeltaX) << 4) + blockX,
(short) blockY,
((chunkZ + chunkDeltaZ) << 4) + blockZ),
updates
);
updateBlock(user, new Position(chunkMinX + blockX, blockY, chunkMinZ + blockZ), updates);
}
}
}
@@ -145,6 +135,7 @@ public class ConnectionData {
} catch (Exception e) {
e.printStackTrace();
}
updates.clear();
}
}
}
@@ -178,17 +169,17 @@ public class ConnectionData {
}
public static void connectBlocks(UserConnection user, Chunk chunk) {
long xOff = chunk.getX() << 4;
long zOff = chunk.getZ() << 4;
int xOff = chunk.getX() << 4;
int zOff = chunk.getZ() << 4;
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
for (int s = 0; s < chunk.getSections().length; s++) {
ChunkSection section = chunk.getSections()[s];
if (section == null) continue;
DataPalette blocks = section.palette(PaletteType.BLOCKS);
boolean willConnect = false;
for (int p = 0; p < section.getPaletteSize(); p++) {
int id = section.getPaletteEntry(p);
for (int p = 0; p < blocks.size(); p++) {
int id = blocks.idByIndex(p);
if (ConnectionData.connects(id)) {
willConnect = true;
break;
@@ -196,24 +187,14 @@ public class ConnectionData {
}
if (!willConnect) continue;
long yOff = i << 4;
int yOff = s << 4;
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getFlatBlock(x, y, z);
ConnectionHandler handler = ConnectionData.getConnectionHandler(block);
if (handler != null) {
block = handler.connect(user, new Position(
(int) (xOff + x),
(short) (yOff + y),
(int) (zOff + z)
), block);
section.setFlatBlock(x, y, z, block);
}
}
}
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx);
ConnectionHandler handler = ConnectionData.getConnectionHandler(id);
if (handler == null) continue;
id = handler.connect(user, new Position(xOff + ChunkSection.xFromIndex(idx), yOff + ChunkSection.yFromIndex(idx), zOff + ChunkSection.zFromIndex(idx)), id);
blocks.setIdAt(idx, id);
}
}
}
@@ -48,6 +48,10 @@ public class BlockConnectionProvider implements Provider {
}
public void unloadChunkSection(UserConnection connection, int chunkX, int chunkY, int chunkZ) {
}
public boolean storesBlocks() {
return false;
}
@@ -47,6 +47,11 @@ public class PacketBlockConnectionProvider extends BlockConnectionProvider {
connection.get(BlockConnectionStorage.class).unloadChunk(x, z);
}
@Override
public void unloadChunkSection(UserConnection connection, int chunkX, int chunkY, int chunkZ) {
connection.get(BlockConnectionStorage.class).unloadSection(chunkX, chunkY, chunkZ);
}
@Override
public boolean storesBlocks() {
return true;
@@ -17,6 +17,7 @@
*/
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.packets;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_13Types;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
@@ -146,6 +147,25 @@ public class EntityPackets {
}
});
protocol.registerClientbound(ClientboundPackets1_12_1.ENTITY_EFFECT, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // Entity id
map(Type.BYTE); // Effect id
map(Type.BYTE); // Amplifier
map(Type.VAR_INT); // Duration
handler(packetWrapper -> {
byte flags = packetWrapper.read(Type.BYTE); // Input Flags
if (Via.getConfig().isNewEffectIndicator())
flags |= 0x04;
packetWrapper.write(Type.BYTE, flags);
});
}
});
metadataRewriter.registerRemoveEntities(ClientboundPackets1_12_1.DESTROY_ENTITIES);
metadataRewriter.registerMetadataRewriter(ClientboundPackets1_12_1.ENTITY_METADATA, Types1_12.METADATA_LIST, Types1_13.METADATA_LIST);
}
@@ -309,8 +309,12 @@ public class InventoryPackets extends ItemRewriter<Protocol1_13To1_12_2> {
if (blockEntityTag.get("Patterns") instanceof ListTag) {
for (Tag pattern : (ListTag) blockEntityTag.get("Patterns")) {
if (pattern instanceof CompoundTag) {
IntTag c = ((CompoundTag) pattern).get("Color");
c.setValue(15 - c.asInt()); // Invert color id
Tag c = ((CompoundTag) pattern).get("Color");
if (c instanceof NumberTag) {
// Invert color id
((CompoundTag) pattern).put("Color",
new IntTag(15 - ((NumberTag) c).asInt()));
}
}
}
}
@@ -27,9 +27,10 @@ import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.Particle;
@@ -83,19 +84,16 @@ public class WorldPackets {
map(Type.VAR_INT); // 0 - Entity ID
map(Type.UUID); // 1 - Entity UUID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
PaintingProvider provider = Via.getManager().getProviders().get(PaintingProvider.class);
String motive = wrapper.read(Type.STRING);
handler(wrapper -> {
PaintingProvider provider = Via.getManager().getProviders().get(PaintingProvider.class);
String motive = wrapper.read(Type.STRING);
Optional<Integer> id = provider.getIntByIdentifier(motive);
Optional<Integer> id = provider.getIntByIdentifier(motive);
if (!id.isPresent() && (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug())) {
Via.getPlatform().getLogger().warning("Could not find painting motive: " + motive + " falling back to default (0)");
}
wrapper.write(Type.VAR_INT, id.orElse(0));
if (!id.isPresent() && (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug())) {
Via.getPlatform().getLogger().warning("Could not find painting motive: " + motive + " falling back to default (0)");
}
wrapper.write(Type.VAR_INT, id.orElse(0));
});
}
});
@@ -107,27 +105,24 @@ public class WorldPackets {
map(Type.UNSIGNED_BYTE); // 1 - Action
map(Type.NBT); // 2 - NBT data
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Position position = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
CompoundTag tag = wrapper.get(Type.NBT, 0);
handler(wrapper -> {
Position position = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
CompoundTag tag = wrapper.get(Type.NBT, 0);
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
int newId = provider.transform(wrapper.user(), position, tag, true);
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
int newId = provider.transform(wrapper.user(), position, tag, true);
if (newId != -1) {
BlockStorage storage = wrapper.user().get(BlockStorage.class);
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
if (newId != -1) {
BlockStorage storage = wrapper.user().get(BlockStorage.class);
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
}
if (action == 5) { // Set type of flower in flower pot
wrapper.cancel(); // Removed
}
if (action == 5) { // Set type of flower in flower pot
wrapper.cancel(); // Removed
}
});
}
@@ -140,43 +135,40 @@ public class WorldPackets {
map(Type.UNSIGNED_BYTE); // Action Id
map(Type.UNSIGNED_BYTE); // Action param
map(Type.VAR_INT); // Block Id - /!\ NOT BLOCK STATE ID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Position pos = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
short param = wrapper.get(Type.UNSIGNED_BYTE, 1);
int blockId = wrapper.get(Type.VAR_INT, 0);
handler(wrapper -> {
Position pos = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
short param = wrapper.get(Type.UNSIGNED_BYTE, 1);
int blockId = wrapper.get(Type.VAR_INT, 0);
if (blockId == 25)
blockId = 73;
else if (blockId == 33)
blockId = 99;
else if (blockId == 29)
blockId = 92;
else if (blockId == 54)
blockId = 142;
else if (blockId == 146)
blockId = 305;
else if (blockId == 130)
blockId = 249;
else if (blockId == 138)
blockId = 257;
else if (blockId == 52)
blockId = 140;
else if (blockId == 209)
blockId = 472;
else if (blockId >= 219 && blockId <= 234)
blockId = blockId - 219 + 483;
if (blockId == 25)
blockId = 73;
else if (blockId == 33)
blockId = 99;
else if (blockId == 29)
blockId = 92;
else if (blockId == 54)
blockId = 142;
else if (blockId == 146)
blockId = 305;
else if (blockId == 130)
blockId = 249;
else if (blockId == 138)
blockId = 257;
else if (blockId == 52)
blockId = 140;
else if (blockId == 209)
blockId = 472;
else if (blockId >= 219 && blockId <= 234)
blockId = blockId - 219 + 483;
if (blockId == 73) { // Note block
PacketWrapper blockChange = wrapper.create(0x0B); // block change
blockChange.write(Type.POSITION, pos);
blockChange.write(Type.VAR_INT, 249 + (action * 24 * 2) + (param * 2));
blockChange.send(Protocol1_13To1_12_2.class);
}
wrapper.set(Type.VAR_INT, 0, blockId);
if (blockId == 73) { // Note block
PacketWrapper blockChange = wrapper.create(0x0B); // block change
blockChange.write(Type.POSITION, pos);
blockChange.write(Type.VAR_INT, 249 + (action * 24 * 2) + (param * 2));
blockChange.send(Protocol1_13To1_12_2.class);
}
wrapper.set(Type.VAR_INT, 0, blockId);
});
}
});
@@ -186,27 +178,24 @@ public class WorldPackets {
public void registerMap() {
map(Type.POSITION);
map(Type.VAR_INT);
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Position position = wrapper.get(Type.POSITION, 0);
int newId = toNewId(wrapper.get(Type.VAR_INT, 0));
handler(wrapper -> {
Position position = wrapper.get(Type.POSITION, 0);
int newId = toNewId(wrapper.get(Type.VAR_INT, 0));
UserConnection userConnection = wrapper.user();
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newId);
newId = ConnectionData.connect(userConnection, position, newId);
}
wrapper.set(Type.VAR_INT, 0, checkStorage(wrapper.user(), position, newId));
if (Via.getConfig().isServersideBlockConnections()) {
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
ConnectionData.update(userConnection, position);
}
UserConnection userConnection = wrapper.user();
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newId);
newId = ConnectionData.connect(userConnection, position, newId);
}
wrapper.set(Type.VAR_INT, 0, checkStorage(wrapper.user(), position, newId));
if (Via.getConfig().isServersideBlockConnections()) {
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
ConnectionData.update(userConnection, position);
}
});
}
});
@@ -217,56 +206,53 @@ public class WorldPackets {
map(Type.INT); // 0 - Chunk X
map(Type.INT); // 1 - Chunk Z
map(Type.BLOCK_CHANGE_RECORD_ARRAY); // 2 - Records
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int chunkX = wrapper.get(Type.INT, 0);
int chunkZ = wrapper.get(Type.INT, 1);
UserConnection userConnection = wrapper.user();
BlockChangeRecord[] records = wrapper.get(Type.BLOCK_CHANGE_RECORD_ARRAY, 0);
// Convert ids
handler(wrapper -> {
int chunkX = wrapper.get(Type.INT, 0);
int chunkZ = wrapper.get(Type.INT, 1);
UserConnection userConnection = wrapper.user();
BlockChangeRecord[] records = wrapper.get(Type.BLOCK_CHANGE_RECORD_ARRAY, 0);
// Convert ids
for (BlockChangeRecord record : records) {
int newBlock = toNewId(record.getBlockId());
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newBlock);
}
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
}
if (Via.getConfig().isServersideBlockConnections()) {
for (BlockChangeRecord record : records) {
int newBlock = toNewId(record.getBlockId());
int blockState = record.getBlockId();
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newBlock);
}
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
}
if (Via.getConfig().isServersideBlockConnections()) {
for (BlockChangeRecord record : records) {
int blockState = record.getBlockId();
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
ConnectionHandler handler = ConnectionData.getConnectionHandler(blockState);
if (handler != null) {
blockState = handler.connect(userConnection, position, blockState);
record.setBlockId(blockState);
}
}
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (BlockChangeRecord record : records) {
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
ConnectionData.update(userConnection, position);
ConnectionHandler handler = ConnectionData.getConnectionHandler(blockState);
if (handler != null) {
blockState = handler.connect(userConnection, position, blockState);
record.setBlockId(blockState);
}
}
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (BlockChangeRecord record : records) {
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
ConnectionData.update(userConnection, position);
}
}
});
}
});
@@ -283,34 +269,31 @@ public class WorldPackets {
map(Type.FLOAT); // Radius
map(Type.INT); // Record Count
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
UserConnection userConnection = wrapper.user();
int x = (int) Math.floor(wrapper.get(Type.FLOAT, 0));
int y = (int) Math.floor(wrapper.get(Type.FLOAT, 1));
int z = (int) Math.floor(wrapper.get(Type.FLOAT, 2));
int recordCount = wrapper.get(Type.INT, 0);
Position[] records = new Position[recordCount];
handler(wrapper -> {
UserConnection userConnection = wrapper.user();
int x = (int) Math.floor(wrapper.get(Type.FLOAT, 0));
int y = (int) Math.floor(wrapper.get(Type.FLOAT, 1));
int z = (int) Math.floor(wrapper.get(Type.FLOAT, 2));
int recordCount = wrapper.get(Type.INT, 0);
Position[] records = new Position[recordCount];
for (int i = 0; i < recordCount; i++) {
Position position = new Position(
x + wrapper.passthrough(Type.BYTE),
(short) (y + wrapper.passthrough(Type.BYTE)),
z + wrapper.passthrough(Type.BYTE));
records[i] = position;
for (int i = 0; i < recordCount; i++) {
Position position = new Position(
x + wrapper.passthrough(Type.BYTE),
(short) (y + wrapper.passthrough(Type.BYTE)),
z + wrapper.passthrough(Type.BYTE));
records[i] = position;
// Set to air
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), 0);
}
// Set to air
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), 0);
}
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (int i = 0; i < recordCount; i++) {
ConnectionData.update(userConnection, records[i]);
}
for (int i = 0; i < recordCount; i++) {
ConnectionData.update(userConnection, records[i]);
}
});
}
@@ -320,13 +303,10 @@ public class WorldPackets {
@Override
public void registerMap() {
if (Via.getConfig().isServersideBlockConnections()) {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int x = wrapper.passthrough(Type.INT);
int z = wrapper.passthrough(Type.INT);
ConnectionData.blockConnectionProvider.unloadChunk(wrapper.user(), x, z);
}
handler(wrapper -> {
int x = wrapper.passthrough(Type.INT);
int z = wrapper.passthrough(Type.INT);
ConnectionData.blockConnectionProvider.unloadChunk(wrapper.user(), x, z);
});
}
}
@@ -336,13 +316,10 @@ public class WorldPackets {
@Override
public void registerMap() {
map(Type.STRING);
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
String sound = wrapper.get(Type.STRING, 0).replace("minecraft:", "");
String newSoundId = NamedSoundRewriter.getNewId(sound);
wrapper.set(Type.STRING, 0, newSoundId);
}
handler(wrapper -> {
String sound = wrapper.get(Type.STRING, 0).replace("minecraft:", "");
String newSoundId = NamedSoundRewriter.getNewId(sound);
wrapper.set(Type.STRING, 0, newSoundId);
});
}
});
@@ -350,218 +327,210 @@ public class WorldPackets {
protocol.registerClientbound(ClientboundPackets1_12_1.CHUNK_DATA, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
BlockStorage storage = wrapper.user().get(BlockStorage.class);
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
BlockStorage storage = wrapper.user().get(BlockStorage.class);
Chunk1_9_3_4Type type = new Chunk1_9_3_4Type(clientWorld);
Chunk1_13Type type1_13 = new Chunk1_13Type(clientWorld);
Chunk chunk = wrapper.read(type);
wrapper.write(type1_13, chunk);
Chunk1_9_3_4Type type = new Chunk1_9_3_4Type(clientWorld);
Chunk1_13Type type1_13 = new Chunk1_13Type(clientWorld);
Chunk chunk = wrapper.read(type);
wrapper.write(type1_13, chunk);
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null)
continue;
for (int s = 0; s < chunk.getSections().length; s++) {
ChunkSection section = chunk.getSections()[s];
if (section == null) continue;
DataPalette blocks = section.palette(PaletteType.BLOCKS);
for (int p = 0; p < section.getPaletteSize(); p++) {
int old = section.getPaletteEntry(p);
int newId = toNewId(old);
section.setPaletteEntry(p, newId);
}
for (int p = 0; p < blocks.size(); p++) {
int old = blocks.idByIndex(p);
int newId = toNewId(old);
blocks.setIdByIndex(p, newId);
}
boolean willSaveToStorage = false;
for (int p = 0; p < section.getPaletteSize(); p++) {
int newId = section.getPaletteEntry(p);
if (storage.isWelcome(newId)) {
willSaveToStorage = true;
break;
}
}
boolean willSaveConnection = false;
if (Via.getConfig().isServersideBlockConnections() && ConnectionData.needStoreBlocks()) {
for (int p = 0; p < section.getPaletteSize(); p++) {
int newId = section.getPaletteEntry(p);
if (ConnectionData.isWelcome(newId)) {
willSaveConnection = true;
storage:
{
if (chunk.isFullChunk()) {
boolean willSave = false;
for (int p = 0; p < blocks.size(); p++) {
if (storage.isWelcome(blocks.idByIndex(p))) {
willSave = true;
break;
}
}
if (!willSave) break storage;
}
if (willSaveToStorage) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getFlatBlock(x, y, z);
if (storage.isWelcome(block)) {
storage.store(new Position(
(x + (chunk.getX() << 4)),
(short) (y + (i << 4)),
(z + (chunk.getZ() << 4))
), block);
}
}
}
}
}
if (willSaveConnection) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getFlatBlock(x, y, z);
if (ConnectionData.isWelcome(block)) {
ConnectionData.blockConnectionProvider.storeBlock(wrapper.user(), x + (chunk.getX() << 4),
y + (i << 4),
z + (chunk.getZ() << 4),
block);
}
}
}
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx);
if (storage.isWelcome(id)) {
storage.store(new Position(ChunkSection.xFromIndex(idx) + (chunk.getX() << 4), ChunkSection.yFromIndex(idx) + (s << 4), ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4)), id);
} else if (!chunk.isFullChunk()) { // Update
storage.remove(new Position(ChunkSection.xFromIndex(idx) + (chunk.getX() << 4), ChunkSection.yFromIndex(idx) + (s << 4), ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4)));
}
}
}
// Rewrite biome id 255 to plains
if (chunk.isBiomeData()) {
int latestBiomeWarn = Integer.MIN_VALUE;
for (int i = 0; i < 256; i++) {
int biome = chunk.getBiomeData()[i];
if (!VALID_BIOMES.contains(biome)) {
if (biome != 255 // is it generated naturally? *shrug*
&& latestBiomeWarn != biome) {
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
Via.getPlatform().getLogger().warning("Received invalid biome id " + biome);
}
latestBiomeWarn = biome;
save_connections:
{
if (!Via.getConfig().isServersideBlockConnections() || !ConnectionData.needStoreBlocks()) break save_connections;
if (!chunk.isFullChunk()) { // Update
ConnectionData.blockConnectionProvider.unloadChunkSection(wrapper.user(), chunk.getX(), s, chunk.getZ());
}
boolean willSave = false;
for (int p = 0; p < blocks.size(); p++) {
if (ConnectionData.isWelcome(blocks.idByIndex(p))) {
willSave = true;
break;
}
}
if (!willSave) break save_connections;
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx);
if (ConnectionData.isWelcome(id)) {
int globalX = ChunkSection.xFromIndex(idx) + (chunk.getX() << 4);
int globalY = ChunkSection.yFromIndex(idx) + (s << 4);
int globalZ = ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4);
ConnectionData.blockConnectionProvider.storeBlock(wrapper.user(), globalX, globalY, globalZ, id);
}
}
}
}
// Rewrite biome id 255 to plains
if (chunk.isBiomeData()) {
int latestBiomeWarn = Integer.MIN_VALUE;
for (int i = 0; i < 256; i++) {
int biome = chunk.getBiomeData()[i];
if (!VALID_BIOMES.contains(biome)) {
if (biome != 255 // is it generated naturally? *shrug*
&& latestBiomeWarn != biome) {
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
Via.getPlatform().getLogger().warning("Received invalid biome id " + biome);
}
chunk.getBiomeData()[i] = 1; // Plains
latestBiomeWarn = biome;
}
chunk.getBiomeData()[i] = 1; // Plains
}
}
}
// Rewrite BlockEntities to normal blocks
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
final Iterator<CompoundTag> iterator = chunk.getBlockEntities().iterator();
while (iterator.hasNext()) {
CompoundTag tag = iterator.next();
int newId = provider.transform(wrapper.user(), null, tag, false);
if (newId != -1) {
int x = ((NumberTag) tag.get("x")).asInt();
int y = ((NumberTag) tag.get("y")).asInt();
int z = ((NumberTag) tag.get("z")).asInt();
// Rewrite BlockEntities to normal blocks
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
final Iterator<CompoundTag> iterator = chunk.getBlockEntities().iterator();
while (iterator.hasNext()) {
CompoundTag tag = iterator.next();
int newId = provider.transform(wrapper.user(), null, tag, false);
if (newId != -1) {
int x = ((NumberTag) tag.get("x")).asInt();
int y = ((NumberTag) tag.get("y")).asInt();
int z = ((NumberTag) tag.get("z")).asInt();
Position position = new Position(x, (short) y, z);
// Store the replacement blocks for blockupdates
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
chunk.getSections()[y >> 4].setFlatBlock(x & 0xF, y & 0xF, z & 0xF, newId);
Position position = new Position(x, (short) y, z);
// Store the replacement blocks for blockupdates
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
final Tag idTag = tag.get("id");
if (idTag instanceof StringTag) {
// No longer block entities
final String id = ((StringTag) idTag).getValue();
if (id.equals("minecraft:noteblock") || id.equals("minecraft:flower_pot")) {
iterator.remove();
}
}
chunk.getSections()[y >> 4].setFlatBlock(x & 0xF, y & 0xF, z & 0xF, newId);
}
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.connectBlocks(wrapper.user(), chunk);
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue;
ConnectionData.updateChunkSectionNeighbours(wrapper.user(), chunk.getX(), chunk.getZ(), i);
final Tag idTag = tag.get("id");
if (idTag instanceof StringTag) {
// No longer block entities
final String id = ((StringTag) idTag).getValue();
if (id.equals("minecraft:noteblock") || id.equals("minecraft:flower_pot")) {
iterator.remove();
}
}
}
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.connectBlocks(wrapper.user(), chunk);
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue;
ConnectionData.updateChunkSectionNeighbours(wrapper.user(), chunk.getX(), chunk.getZ(), i);
}
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_12_1.SPAWN_PARTICLE, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Particle ID
map(Type.BOOLEAN); // 1 - Long Distance
map(Type.FLOAT); // 2 - X
map(Type.FLOAT); // 3 - Y
map(Type.FLOAT); // 4 - Z
map(Type.FLOAT); // 5 - Offset X
map(Type.FLOAT); // 6 - Offset Y
map(Type.FLOAT); // 7 - Offset Z
map(Type.FLOAT); // 8 - Particle Data
map(Type.INT); // 9 - Particle Count
protocol.registerClientbound(ClientboundPackets1_12_1.SPAWN_PARTICLE, new
handler(new PacketHandler() {
PacketRemapper() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int particleId = wrapper.get(Type.INT, 0);
public void registerMap() {
map(Type.INT); // 0 - Particle ID
map(Type.BOOLEAN); // 1 - Long Distance
map(Type.FLOAT); // 2 - X
map(Type.FLOAT); // 3 - Y
map(Type.FLOAT); // 4 - Z
map(Type.FLOAT); // 5 - Offset X
map(Type.FLOAT); // 6 - Offset Y
map(Type.FLOAT); // 7 - Offset Z
map(Type.FLOAT); // 8 - Particle Data
map(Type.INT); // 9 - Particle Count
// Get the data (Arrays are overrated)
int dataCount = 0;
// Particles with 1 data [BlockCrack,BlockDust,FallingDust]
if (particleId == 37 || particleId == 38 || particleId == 46)
dataCount = 1;
// Particles with 2 data [IconCrack]
else if (particleId == 36)
dataCount = 2;
handler(wrapper -> {
int particleId = wrapper.get(Type.INT, 0);
Integer[] data = new Integer[dataCount];
for (int i = 0; i < data.length; i++)
data[i] = wrapper.read(Type.VAR_INT);
// Get the data (Arrays are overrated)
int dataCount = 0;
// Particles with 1 data [BlockCrack,BlockDust,FallingDust]
if (particleId == 37 || particleId == 38 || particleId == 46)
dataCount = 1;
// Particles with 2 data [IconCrack]
else if (particleId == 36)
dataCount = 2;
Particle particle = ParticleRewriter.rewriteParticle(particleId, data);
Integer[] data = new Integer[dataCount];
for (int i = 0; i < data.length; i++)
data[i] = wrapper.read(Type.VAR_INT);
// Cancel if null or completely removed
if (particle == null || particle.getId() == -1) {
wrapper.cancel();
return;
}
Particle particle = ParticleRewriter.rewriteParticle(particleId, data);
//Handle reddust particle color
if (particle.getId() == 11) {
int count = wrapper.get(Type.INT, 1);
float speed = wrapper.get(Type.FLOAT, 6);
// Only handle for count = 0
if (count == 0) {
wrapper.set(Type.INT, 1, 1);
wrapper.set(Type.FLOAT, 6, 0f);
// Cancel if null or completely removed
if (particle == null || particle.getId() == -1) {
wrapper.cancel();
return;
}
List<Particle.ParticleData> arguments = particle.getArguments();
for (int i = 0; i < 3; i++) {
//RGB values are represented by the X/Y/Z offset
float colorValue = wrapper.get(Type.FLOAT, i + 3) * speed;
if (colorValue == 0 && i == 0) {
// https://minecraft.gamepedia.com/User:Alphappy/reddust
colorValue = 1;
//Handle reddust particle color
if (particle.getId() == 11) {
int count = wrapper.get(Type.INT, 1);
float speed = wrapper.get(Type.FLOAT, 6);
// Only handle for count = 0
if (count == 0) {
wrapper.set(Type.INT, 1, 1);
wrapper.set(Type.FLOAT, 6, 0f);
List<Particle.ParticleData> arguments = particle.getArguments();
for (int i = 0; i < 3; i++) {
//RGB values are represented by the X/Y/Z offset
float colorValue = wrapper.get(Type.FLOAT, i + 3) * speed;
if (colorValue == 0 && i == 0) {
// https://minecraft.gamepedia.com/User:Alphappy/reddust
colorValue = 1;
}
arguments.get(i).setValue(colorValue);
wrapper.set(Type.FLOAT, i + 3, 0f);
}
arguments.get(i).setValue(colorValue);
wrapper.set(Type.FLOAT, i + 3, 0f);
}
}
}
wrapper.set(Type.INT, 0, particle.getId());
for (Particle.ParticleData particleData : particle.getArguments())
wrapper.write(particleData.getType(), particleData.getValue());
wrapper.set(Type.INT, 0, particle.getId());
for (Particle.ParticleData particleData : particle.getArguments())
wrapper.write(particleData.getType(), particleData.getValue());
});
}
});
}
});
}
public static int toNewId(int oldId) {
@@ -51,8 +51,8 @@ public class BannerHandler implements BlockEntityProvider.BlockEntityHandler {
Tag base = tag.get("Base");
int color = 0;
if (base != null) {
color = ((NumberTag) tag.get("Base")).asInt();
if (base instanceof NumberTag) {
color = ((NumberTag) base).asInt();
}
// Standing banner
if (blockId >= BANNER_START && blockId <= BANNER_STOP) {
@@ -64,8 +64,9 @@ public class BannerHandler implements BlockEntityProvider.BlockEntityHandler {
Via.getPlatform().getLogger().warning("Why does this block have the banner block entity? :(" + tag);
}
if (tag.get("Patterns") instanceof ListTag) {
for (Tag pattern : (ListTag) tag.get("Patterns")) {
Tag patterns = tag.get("Patterns");
if (patterns instanceof ListTag) {
for (Tag pattern : (ListTag) patterns) {
if (pattern instanceof CompoundTag) {
Tag c = ((CompoundTag) pattern).get("Color");
if (c instanceof IntTag) {
@@ -42,7 +42,7 @@ public class BedHandler implements BlockEntityProvider.BlockEntityHandler {
int blockId = storage.get(position).getOriginal() - 972 + 748;
Tag color = tag.get("color");
if (color != null) {
if (color instanceof NumberTag) {
blockId += (((NumberTag) color).asInt() * 16);
}
@@ -43,11 +43,12 @@ public class SkullHandler implements BlockEntityProvider.BlockEntityHandler {
int id = storage.get(position).getOriginal();
if (id >= SKULL_WALL_START && id <= SKULL_END) {
Tag skullType = tag.get("SkullType");
if (skullType != null) {
id += ((NumberTag) tag.get("SkullType")).asInt() * 20;
if (skullType instanceof NumberTag) {
id += ((NumberTag) skullType).asInt() * 20;
}
if (tag.contains("Rot")) {
id += ((NumberTag) tag.get("Rot")).asInt();
Tag rot = tag.get("Rot");
if (rot instanceof NumberTag) {
id += ((NumberTag) rot).asInt();
}
} else {
Via.getPlatform().getLogger().warning("Why does this block have the skull block entity? " + tag);
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.providers.block
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.data.EntityNameRewriter;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.providers.BlockEntityProvider;
@@ -26,11 +27,11 @@ import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.providers.BlockE
public class SpawnerHandler implements BlockEntityProvider.BlockEntityHandler {
@Override
public int transform(UserConnection user, CompoundTag tag) {
if (tag.contains("SpawnData") && tag.get("SpawnData") instanceof CompoundTag) {
CompoundTag data = tag.get("SpawnData");
if (data.contains("id") && data.get("id") instanceof StringTag) {
StringTag s = data.get("id");
s.setValue(EntityNameRewriter.rewrite(s.getValue()));
Tag data = tag.get("SpawnData");
if (data instanceof CompoundTag) {
Tag id = ((CompoundTag) data).get("id");
if (id instanceof StringTag) {
((StringTag) id).setValue(EntityNameRewriter.rewrite(((StringTag) id).getValue()));
}
}
@@ -111,11 +111,15 @@ public class BlockConnectionStorage implements StorableObject {
}
public void unloadChunk(int x, int z) {
for (int y = 0; y < 256; y += 16) {
blockStorage.remove(getChunkSectionIndex(x << 4, y, z << 4));
for (int y = 0; y < 16; y ++) {
unloadSection(x, y, z);
}
}
public void unloadSection(int x, int y, int z) {
blockStorage.remove(getChunkSectionIndex(x << 4, y << 4, z << 4));
}
private Pair<byte[], NibbleArray> getChunkSection(long index, boolean requireNibbleArray) {
Pair<byte[], NibbleArray> map = blockStorage.get(index);
if (map == null) {
@@ -24,7 +24,9 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.NibbleArray;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
@@ -154,15 +156,16 @@ public class WorldPackets {
for (int s = 0; s < chunk.getSections().length; s++) {
ChunkSection section = chunk.getSections()[s];
if (section == null) continue;
DataPalette blocks = section.palette(PaletteType.BLOCKS);
boolean hasBlock = false;
for (int i = 0; i < section.getPaletteSize(); i++) {
int old = section.getPaletteEntry(i);
for (int i = 0; i < blocks.size(); i++) {
int old = blocks.idByIndex(i);
int newId = protocol.getMappingData().getNewBlockStateId(old);
if (!hasBlock && newId != air && newId != voidAir && newId != caveAir) { // air, void_air, cave_air
hasBlock = true;
}
section.setPaletteEntry(i, newId);
blocks.setIdByIndex(i, newId);
}
if (!hasBlock) {
section.setNonAirBlocksCount(0);
@@ -170,23 +173,25 @@ public class WorldPackets {
}
int nonAirBlockCount = 0;
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
int id = section.getFlatBlock(x, y, z);
if (id != air && id != voidAir && id != caveAir) {
nonAirBlockCount++;
worldSurface[x + z * 16] = y + s * 16 + 1; // +1 (top of the block)
}
if (protocol.getMappingData().getMotionBlocking().contains(id)) {
motionBlocking[x + z * 16] = y + s * 16 + 1; // +1 (top of the block)
}
int sy = s << 4;
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx);
if (id == air || id == voidAir || id == caveAir) continue;
nonAirBlockCount++;
// Manually update light for non full blocks (block light must not be sent)
if (Via.getConfig().isNonFullBlockLightFix() && protocol.getMappingData().getNonFullBlocks().contains(id)) {
setNonFullLight(chunk, section, s, x, y, z);
}
}
int xz = idx & 0xFF;
int y = ChunkSection.yFromIndex(idx);
worldSurface[xz] = sy + y + 1; // +1 (top of the block)
if (protocol.getMappingData().getMotionBlocking().contains(id)) {
motionBlocking[xz] = sy + y + 1; // +1 (top of the block)
}
// Manually update light for non-full blocks (block light must not be sent)
if (Via.getConfig().isNonFullBlockLightFix() && protocol.getMappingData().getNonFullBlocks().contains(id)) {
int x = ChunkSection.xFromIndex(idx);
int z = ChunkSection.zFromIndex(idx);
setNonFullLight(chunk, section, s, x, y, z);
}
}
@@ -377,7 +382,7 @@ public class WorldPackets {
neighbourY = 15;
}
if (ySection == 16 || ySection == -1) continue;
if (ySection == chunk.getSections().length || ySection == -1) continue;
ChunkSection newSection = chunk.getSections()[ySection];
if (newSection == null) continue;
@@ -34,7 +34,7 @@ public class InventoryPackets extends ItemRewriter<Protocol1_15To1_14_4> {
public void registerPackets() {
registerSetCooldown(ClientboundPackets1_14.COOLDOWN);
registerWindowItems(ClientboundPackets1_14.WINDOW_ITEMS, Type.FLAT_VAR_INT_ITEM_ARRAY);
registerTradeList(ClientboundPackets1_14.TRADE_LIST, Type.FLAT_VAR_INT_ITEM);
registerTradeList(ClientboundPackets1_14.TRADE_LIST);
registerSetSlot(ClientboundPackets1_14.SET_SLOT, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipment(ClientboundPackets1_14.ENTITY_EQUIPMENT, Type.FLAT_VAR_INT_ITEM);
registerAdvancements(ClientboundPackets1_14.ADVANCEMENTS, Type.FLAT_VAR_INT_ITEM);
@@ -35,9 +35,9 @@ public class InventoryPackets extends ItemRewriter<Protocol1_16_2To1_16_1> {
public void registerPackets() {
registerSetCooldown(ClientboundPackets1_16.COOLDOWN);
registerWindowItems(ClientboundPackets1_16.WINDOW_ITEMS, Type.FLAT_VAR_INT_ITEM_ARRAY);
registerTradeList(ClientboundPackets1_16.TRADE_LIST, Type.FLAT_VAR_INT_ITEM);
registerTradeList(ClientboundPackets1_16.TRADE_LIST);
registerSetSlot(ClientboundPackets1_16.SET_SLOT, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_16.ENTITY_EQUIPMENT, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_16.ENTITY_EQUIPMENT);
registerAdvancements(ClientboundPackets1_16.ADVANCEMENTS, Type.FLAT_VAR_INT_ITEM);
protocol.registerClientbound(ClientboundPackets1_16.UNLOCK_RECIPES, new PacketRemapper() {
@@ -60,6 +60,7 @@ public class Protocol1_16To1_15_2 extends AbstractProtocol<ClientboundPackets1_1
public static final MappingData MAPPINGS = new MappingData();
private final EntityRewriter metadataRewriter = new MetadataRewriter1_16To1_15_2(this);
private final ItemRewriter itemRewriter = new InventoryPackets(this);
private final ComponentRewriter componentRewriter = new TranslationMappings(this);
private TagRewriter tagRewriter;
public Protocol1_16To1_15_2() {
@@ -131,7 +132,6 @@ public class Protocol1_16To1_15_2 extends AbstractProtocol<ClientboundPackets1_1
}
});
ComponentRewriter componentRewriter = new TranslationMappings(this);
// Handle (relevant) component cases for translatable and score changes
registerClientbound(ClientboundPackets1_15.CHAT_MESSAGE, new PacketRemapper() {
@Override
@@ -301,4 +301,8 @@ public class Protocol1_16To1_15_2 extends AbstractProtocol<ClientboundPackets1_1
public ItemRewriter getItemRewriter() {
return itemRewriter;
}
public ComponentRewriter getComponentRewriter() {
return componentRewriter;
}
}
@@ -109,7 +109,7 @@ public class InventoryPackets extends ItemRewriter<Protocol1_16To1_15_2> {
registerSetCooldown(ClientboundPackets1_15.COOLDOWN);
registerWindowItems(ClientboundPackets1_15.WINDOW_ITEMS, Type.FLAT_VAR_INT_ITEM_ARRAY);
registerTradeList(ClientboundPackets1_15.TRADE_LIST, Type.FLAT_VAR_INT_ITEM);
registerTradeList(ClientboundPackets1_15.TRADE_LIST);
registerSetSlot(ClientboundPackets1_15.SET_SLOT, Type.FLAT_VAR_INT_ITEM);
registerAdvancements(ClientboundPackets1_15.ADVANCEMENTS, Type.FLAT_VAR_INT_ITEM);
@@ -155,8 +155,9 @@ public class InventoryPackets extends ItemRewriter<Protocol1_16To1_15_2> {
public Item handleItemToClient(Item item) {
if (item == null) return null;
if (item.identifier() == 771 && item.tag() != null) {
CompoundTag tag = item.tag();
CompoundTag tag = item.tag();
if (item.identifier() == 771 && tag != null) {
Tag ownerTag = tag.get("SkullOwner");
if (ownerTag instanceof CompoundTag) {
CompoundTag ownerCompundTag = (CompoundTag) ownerTag;
@@ -166,6 +167,17 @@ public class InventoryPackets extends ItemRewriter<Protocol1_16To1_15_2> {
ownerCompundTag.put("Id", new IntArrayTag(UUIDIntArrayType.uuidToIntArray(id)));
}
}
} else if (item.identifier() == 759 && tag != null) {
Tag pages = tag.get("pages");
if (pages instanceof ListTag) {
for (Tag pageTag : (ListTag) pages) {
if (!(pageTag instanceof StringTag)) {
continue;
}
StringTag page = (StringTag) pageTag;
page.setValue(protocol.getComponentRewriter().processText(page.getValue()).toString());
}
}
}
oldToNewAttributes(item);
@@ -22,6 +22,7 @@ import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
import com.github.steveice10.opennbt.tag.builtin.LongArrayTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
@@ -85,7 +86,7 @@ public class WorldPackets {
if (chunk.getBlockEntities() == null) return;
for (CompoundTag blockEntity : chunk.getBlockEntities()) {
handleBlockEntity(blockEntity);
handleBlockEntity(protocol, blockEntity);
}
});
}
@@ -98,7 +99,7 @@ public class WorldPackets {
Position position = wrapper.passthrough(Type.POSITION1_14);
short action = wrapper.passthrough(Type.UNSIGNED_BYTE);
CompoundTag tag = wrapper.passthrough(Type.NBT);
handleBlockEntity(tag);
handleBlockEntity(protocol, tag);
});
}
});
@@ -106,7 +107,7 @@ public class WorldPackets {
blockRewriter.registerEffect(ClientboundPackets1_15.EFFECT, 1010, 2001);
}
private static void handleBlockEntity(CompoundTag compoundTag) {
private static void handleBlockEntity(Protocol1_16To1_15_2 protocol, CompoundTag compoundTag) {
StringTag idTag = compoundTag.get("id");
if (idTag == null) return;
@@ -132,6 +133,25 @@ public class WorldPackets {
skullOwnerTag.put(entry.getKey(), entry.getValue());
}
compoundTag.put("SkullOwner", skullOwnerTag);
} else if (id.equals("minecraft:sign")) {
for (int i = 1; i <= 4; i++) {
Tag line = compoundTag.get("Text" + i);
if (line instanceof StringTag) {
JsonElement text = protocol.getComponentRewriter().processText(((StringTag) line).getValue());
compoundTag.put("Text" + i, new StringTag(text.toString()));
}
}
} else if (id.equals("minecraft:mob_spawner")) {
Tag spawnDataTag = compoundTag.get("SpawnData");
if (spawnDataTag instanceof CompoundTag) {
Tag spawnDataIdTag = ((CompoundTag) spawnDataTag).get("id");
if (spawnDataIdTag instanceof StringTag) {
StringTag spawnDataIdStringTag = ((StringTag) spawnDataIdTag);
if (spawnDataIdStringTag.getValue().equals("minecraft:zombie_pigman")) {
spawnDataIdStringTag.setValue("minecraft:zombified_piglin");
}
}
}
}
}
}
@@ -40,10 +40,10 @@ public final class InventoryPackets extends ItemRewriter<Protocol1_17To1_16_4> {
public void registerPackets() {
registerSetCooldown(ClientboundPackets1_16_2.COOLDOWN);
registerWindowItems(ClientboundPackets1_16_2.WINDOW_ITEMS, Type.FLAT_VAR_INT_ITEM_ARRAY);
registerTradeList(ClientboundPackets1_16_2.TRADE_LIST, Type.FLAT_VAR_INT_ITEM);
registerTradeList(ClientboundPackets1_16_2.TRADE_LIST);
registerSetSlot(ClientboundPackets1_16_2.SET_SLOT, Type.FLAT_VAR_INT_ITEM);
registerAdvancements(ClientboundPackets1_16_2.ADVANCEMENTS, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_16_2.ENTITY_EQUIPMENT, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_16_2.ENTITY_EQUIPMENT);
registerSpawnParticle(ClientboundPackets1_16_2.SPAWN_PARTICLE, Type.FLAT_VAR_INT_ITEM, Type.DOUBLE);
new RecipeRewriter1_16(protocol).registerDefaultHandler(ClientboundPackets1_16_2.DECLARE_RECIPES);
@@ -35,11 +35,11 @@ public final class InventoryPackets extends ItemRewriter<Protocol1_18To1_17_1> {
@Override
public void registerPackets() {
registerSetCooldown(ClientboundPackets1_17_1.COOLDOWN);
registerWindowItems1_17_1(ClientboundPackets1_17_1.WINDOW_ITEMS, Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT, Type.FLAT_VAR_INT_ITEM);
registerTradeList(ClientboundPackets1_17_1.TRADE_LIST, Type.FLAT_VAR_INT_ITEM);
registerSetSlot1_17_1(ClientboundPackets1_17_1.SET_SLOT, Type.FLAT_VAR_INT_ITEM);
registerWindowItems1_17_1(ClientboundPackets1_17_1.WINDOW_ITEMS);
registerTradeList(ClientboundPackets1_17_1.TRADE_LIST);
registerSetSlot1_17_1(ClientboundPackets1_17_1.SET_SLOT);
registerAdvancements(ClientboundPackets1_17_1.ADVANCEMENTS, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_17_1.ENTITY_EQUIPMENT, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_17_1.ENTITY_EQUIPMENT);
protocol.registerClientbound(ClientboundPackets1_17_1.EFFECT, new PacketRemapper() {
@Override
@@ -99,7 +99,7 @@ public final class InventoryPackets extends ItemRewriter<Protocol1_18To1_17_1> {
new RecipeRewriter1_16(protocol).registerDefaultHandler(ClientboundPackets1_17_1.DECLARE_RECIPES);
registerClickWindow1_17_1(ServerboundPackets1_17.CLICK_WINDOW, Type.FLAT_VAR_INT_ITEM);
registerClickWindow1_17_1(ServerboundPackets1_17.CLICK_WINDOW);
registerCreativeInvAction(ServerboundPackets1_17.CREATIVE_INVENTORY_ACTION, Type.FLAT_VAR_INT_ITEM);
}
}
@@ -20,6 +20,7 @@ package com.viaversion.viaversion.protocols.protocol1_18to1_17_1.packets;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
@@ -212,8 +213,8 @@ public final class WorldPackets {
private static void handleSpawners(int typeId, final CompoundTag tag) {
if (typeId == 8) {
final CompoundTag entity = tag.get("SpawnData");
if (entity != null) {
final Tag entity = tag.get("SpawnData");
if (entity instanceof CompoundTag) {
final CompoundTag spawnData = new CompoundTag();
tag.put("SpawnData", spawnData);
spawnData.put("entity", entity);
@@ -15,14 +15,25 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.classgenerator.generated;
package com.viaversion.viaversion.protocols.protocol1_19_1to1_19;
import com.viaversion.viaversion.api.connection.UserConnection;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import com.google.gson.JsonElement;
public interface HandlerConstructor {
public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder);
public final class ChatDecorationResult {
public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder);
private final JsonElement content;
private final boolean overlay;
public ChatDecorationResult(final JsonElement content, final boolean overlay) {
this.content = content;
this.overlay = overlay;
}
public JsonElement content() {
return content;
}
public boolean overlay() {
return overlay;
}
}
@@ -116,14 +116,20 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
// Back to system chat
final JsonElement signedContent = wrapper.read(Type.COMPONENT);
final JsonElement unsignedContent = wrapper.read(Type.OPTIONAL_COMPONENT);
final int chatType = wrapper.read(Type.VAR_INT);
final int chatTypeId = wrapper.read(Type.VAR_INT);
wrapper.read(Type.UUID); // Sender UUID
final JsonElement senderName = wrapper.read(Type.COMPONENT);
final JsonElement teamName = wrapper.read(Type.OPTIONAL_COMPONENT);
if (!decorateChatMessage(wrapper, chatType, senderName, teamName, unsignedContent != null ? unsignedContent : signedContent)) {
final CompoundTag chatType = wrapper.user().get(ChatTypeStorage.class).chatType(chatTypeId);
final ChatDecorationResult decorationResult = decorateChatMessage(chatType, chatTypeId, senderName, teamName, unsignedContent != null ? unsignedContent : signedContent);
if (decorationResult == null) {
wrapper.cancel();
return;
}
wrapper.write(Type.COMPONENT, decorationResult.content());
wrapper.write(Type.BOOLEAN, decorationResult.overlay());
});
read(Type.LONG); // Timestamp
read(Type.LONG); // Salt
@@ -249,6 +255,31 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
});
}
});
registerClientbound(State.LOGIN, ClientboundLoginPackets.CUSTOM_QUERY.getId(), ClientboundLoginPackets.CUSTOM_QUERY.getId(), new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT);
map(Type.STRING);
handler(wrapper -> {
String identifier = wrapper.get(Type.STRING, 0);
if (identifier.equals("velocity:player_info")) {
byte[] data = wrapper.passthrough(Type.REMAINING_BYTES);
// Velocity modern forwarding version above 1 includes the players public key.
// This is an issue because the server will expect a 1.19 key and receive a 1.19.1 key.
// Velocity modern forwarding versions: https://github.com/PaperMC/Velocity/blob/1a3fba4250553702d9dcd05731d04347bfc24c9f/proxy/src/main/java/com/velocitypowered/proxy/connection/VelocityConstants.java#L27-L29
// And the version can be specified with a single byte: https://github.com/PaperMC/Velocity/blob/1a3fba4250553702d9dcd05731d04347bfc24c9f/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java#L88
if (data.length == 1 && data[0] > 1) {
data[0] = 1;
} else if (data.length == 0) { // Or the version is omitted (default version would be used)
data = new byte[]{1};
wrapper.set(Type.REMAINING_BYTES, 0, data);
} else {
Via.getPlatform().getLogger().warning("Received unexpected data in velocity:player_info (length=" + data.length + ")");
}
}
});
}
});
}
@Override
@@ -256,11 +287,10 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
connection.put(new ChatTypeStorage());
}
private boolean decorateChatMessage(final PacketWrapper wrapper, final int chatTypeId, final JsonElement senderName, @Nullable final JsonElement teamName, final JsonElement message) {
final CompoundTag chatType = wrapper.user().get(ChatTypeStorage.class).chatType(chatTypeId);
public static @Nullable ChatDecorationResult decorateChatMessage(final CompoundTag chatType, final int chatTypeId, final JsonElement senderName, @Nullable final JsonElement teamName, final JsonElement message) {
if (chatType == null) {
Via.getPlatform().getLogger().warning("Chat message has unknown chat type id " + chatTypeId + ". Message: " + message);
return false;
return null;
}
CompoundTag chatData = chatType.<CompoundTag>get("element").get("chat");
@@ -269,7 +299,7 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
chatData = chatType.<CompoundTag>get("element").get("overlay");
if (chatData == null) {
// Either narration or something we don't know
return false;
return null;
}
overlay = true;
@@ -277,9 +307,7 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
final CompoundTag decoaration = chatData.get("decoration");
if (decoaration == null) {
wrapper.write(Type.COMPONENT, message);
wrapper.write(Type.BOOLEAN, overlay);
return true;
return new ChatDecorationResult(message, overlay);
}
final String translationKey = (String) decoaration.get("translation_key").getValue();
@@ -331,9 +359,6 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
}
componentBuilder.args(arguments);
}
wrapper.write(Type.COMPONENT, GsonComponentSerializer.gson().serializeToTree(componentBuilder.build()));
wrapper.write(Type.BOOLEAN, overlay);
return true;
return new ChatDecorationResult(GsonComponentSerializer.gson().serializeToTree(componentBuilder.build()), overlay);
}
}
@@ -0,0 +1,141 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
public enum ClientboundPackets1_19_3 implements ClientboundPacketType {
SPAWN_ENTITY, // 0x00
SPAWN_EXPERIENCE_ORB, // 0x01
SPAWN_PLAYER, // 0x02
ENTITY_ANIMATION, // 0x03
STATISTICS, // 0x04
BLOCK_CHANGED_ACK, // 0x05
BLOCK_BREAK_ANIMATION, // 0x06
BLOCK_ENTITY_DATA, // 0x07
BLOCK_ACTION, // 0x08
BLOCK_CHANGE, // 0x09
BOSSBAR, // 0x0A
SERVER_DIFFICULTY, // 0x0B
CLEAR_TITLES, // 0x0C
TAB_COMPLETE, // 0x0D
DECLARE_COMMANDS, // 0x0E
CLOSE_WINDOW, // 0x0F
WINDOW_ITEMS, // 0x10
WINDOW_PROPERTY, // 0x11
SET_SLOT, // 0x12
COOLDOWN, // 0x13
CUSTOM_CHAT_COMPLETIONS, // 0x14
PLUGIN_MESSAGE, // 0x15
DELETE_CHAT_MESSAGE, // 0x16
DISCONNECT, // 0x17
DISGUISED_CHAT, // 0x18
ENTITY_STATUS, // 0x19
EXPLOSION, // 0x1A
UNLOAD_CHUNK, // 0x1B
GAME_EVENT, // 0x1C
OPEN_HORSE_WINDOW, // 0x1D
WORLD_BORDER_INIT, // 0x1E
KEEP_ALIVE, // 0x1F
CHUNK_DATA, // 0x20
EFFECT, // 0x21
SPAWN_PARTICLE, // 0x22
UPDATE_LIGHT, // 0x23
JOIN_GAME, // 0x24
MAP_DATA, // 0x25
TRADE_LIST, // 0x26
ENTITY_POSITION, // 0x27
ENTITY_POSITION_AND_ROTATION, // 0x28
ENTITY_ROTATION, // 0x29
VEHICLE_MOVE, // 0x2A
OPEN_BOOK, // 0x2B
OPEN_WINDOW, // 0x2C
OPEN_SIGN_EDITOR, // 0x2D
PING, // 0x2E
CRAFT_RECIPE_RESPONSE, // 0x2F
PLAYER_ABILITIES, // 0x30
PLAYER_CHAT, // 0x31
COMBAT_END, // 0x32
COMBAT_ENTER, // 0x33
COMBAT_KILL, // 0x34
PLAYER_INFO_REMOVE, // 0x35
PLAYER_INFO_UPDATE, // 0x36
FACE_PLAYER, // 0x37
PLAYER_POSITION, // 0x38
UNLOCK_RECIPES, // 0x39
REMOVE_ENTITIES, // 0x3A
REMOVE_ENTITY_EFFECT, // 0x3B
RESOURCE_PACK, // 0x3C
RESPAWN, // 0x3D
ENTITY_HEAD_LOOK, // 0x3E
MULTI_BLOCK_CHANGE, // 0x3F
SELECT_ADVANCEMENTS_TAB, // 0x40
SERVER_DATA, // 0x41
ACTIONBAR, // 0x42
WORLD_BORDER_CENTER, // 0x43
WORLD_BORDER_LERP_SIZE, // 0x44
WORLD_BORDER_SIZE, // 0x45
WORLD_BORDER_WARNING_DELAY, // 0x46
WORLD_BORDER_WARNING_DISTANCE, // 0x47
CAMERA, // 0x48
HELD_ITEM_CHANGE, // 0x49
UPDATE_VIEW_POSITION, // 0x4A
UPDATE_VIEW_DISTANCE, // 0x4B
SPAWN_POSITION, // 0x4C
DISPLAY_SCOREBOARD, // 0x4D
ENTITY_METADATA, // 0x4E
ATTACH_ENTITY, // 0x4F
ENTITY_VELOCITY, // 0x50
ENTITY_EQUIPMENT, // 0x51
SET_EXPERIENCE, // 0x52
UPDATE_HEALTH, // 0x53
SCOREBOARD_OBJECTIVE, // 0x54
SET_PASSENGERS, // 0x55
TEAMS, // 0x56
UPDATE_SCORE, // 0x57
SET_SIMULATION_DISTANCE, // 0x58
TITLE_SUBTITLE, // 0x59
TIME_UPDATE, // 0x5A
TITLE_TEXT, // 0x5B
TITLE_TIMES, // 0x5C
ENTITY_SOUND, // 0x5D
SOUND, // 0x5E
STOP_SOUND, // 0x5F
SYSTEM_CHAT, // 0x60
TAB_LIST, // 0x61
NBT_QUERY, // 0x62
COLLECT_ITEM, // 0x63
ENTITY_TELEPORT, // 0x64
ADVANCEMENTS, // 0x65
ENTITY_PROPERTIES, // 0x66
UPDATE_ENABLED_FEATURES, // 0x67
ENTITY_EFFECT, // 0x68
DECLARE_RECIPES, // 0x69
TAGS; // 0x6A
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
}
@@ -0,0 +1,320 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_19_3Types;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.rewriter.EntityRewriter;
import com.viaversion.viaversion.api.rewriter.ItemRewriter;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.BitSetType;
import com.viaversion.viaversion.api.type.types.ByteArrayType;
import com.viaversion.viaversion.api.type.types.minecraft.ParticleType;
import com.viaversion.viaversion.api.type.types.version.Types1_19_3;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.libs.kyori.adventure.text.Component;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;
import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.ClientboundPackets1_19_1;
import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.ServerboundPackets1_19_1;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.packets.EntityPackets;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.storage.ReceivedMessagesStorage;
import com.viaversion.viaversion.rewriter.CommandRewriter;
import com.viaversion.viaversion.rewriter.SoundRewriter;
import com.viaversion.viaversion.rewriter.StatisticsRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import java.util.UUID;
public final class Protocol1_19_3To1_19_1 extends AbstractProtocol<ClientboundPackets1_19_1, ClientboundPackets1_19_3, ServerboundPackets1_19_1, ServerboundPackets1_19_3> {
public static final MappingData MAPPINGS = new MappingDataBase("1.19", "1.19.3", true);
private static final ByteArrayType.OptionalByteArrayType OPTIONAL_MESSAGE_SIGNATURE_BYTES_TYPE = new ByteArrayType.OptionalByteArrayType(256);
private static final UUID ZERO_UUID = new UUID(0, 0);
private static final byte[] EMPTY_BYTES = new byte[0];
private final EntityPackets entityRewriter = new EntityPackets(this);
private final InventoryPackets itemRewriter = new InventoryPackets(this);
public Protocol1_19_3To1_19_1() {
super(ClientboundPackets1_19_1.class, ClientboundPackets1_19_3.class, ServerboundPackets1_19_1.class, ServerboundPackets1_19_3.class);
}
@Override
protected void registerPackets() {
final TagRewriter tagRewriter = new TagRewriter(this);
tagRewriter.addEmptyTags(RegistryType.ITEM, "minecraft:bookshelf_books", "minecraft:hanging_signs", "minecraft:stripped_logs");
tagRewriter.addEmptyTags(RegistryType.BLOCK, "minecraft:all_hanging_signs", "minecraft:ceiling_hanging_signs", "minecraft:invalid_spawn_inside",
"minecraft:stripped_logs", "minecraft:wall_hanging_signs");
tagRewriter.registerGeneric(ClientboundPackets1_19_1.TAGS);
entityRewriter.register();
itemRewriter.register();
final SoundRewriter soundRewriter = new SoundRewriter(this);
registerClientbound(ClientboundPackets1_19_1.ENTITY_SOUND, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // Sound id
handler(soundRewriter.getSoundHandler());
handler(wrapper -> {
// 0 means a resource location will be written
final int soundId = wrapper.get(Type.VAR_INT, 0);
wrapper.set(Type.VAR_INT, 0, soundId + 1);
});
}
});
registerClientbound(ClientboundPackets1_19_1.SOUND, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // Sound id
handler(soundRewriter.getSoundHandler());
handler(wrapper -> {
// 0 means a resource location will be written
final int soundId = wrapper.get(Type.VAR_INT, 0);
wrapper.set(Type.VAR_INT, 0, soundId + 1);
});
}
});
registerClientbound(ClientboundPackets1_19_1.NAMED_SOUND, ClientboundPackets1_19_3.SOUND, new PacketRemapper() {
@Override
public void registerMap() {
handler(wrapper -> {
wrapper.write(Type.VAR_INT, 0);
wrapper.passthrough(Type.STRING); // Sound identifier
wrapper.write(Type.OPTIONAL_FLOAT, null); // No fixed range
});
}
});
new StatisticsRewriter(this).register(ClientboundPackets1_19_1.STATISTICS);
final CommandRewriter commandRewriter = new CommandRewriter(this) {
@Override
public void handleArgument(final PacketWrapper wrapper, final String argumentType) throws Exception {
switch (argumentType) {
case "minecraft:item_enchantment":
wrapper.write(Type.STRING, "minecraft:enchantment");
break;
case "minecraft:mob_effect":
wrapper.write(Type.STRING, "minecraft:mob_effect");
break;
case "minecraft:entity_summon":
wrapper.write(Type.STRING, "minecraft:entity_type");
break;
default:
super.handleArgument(wrapper, argumentType);
break;
}
}
@Override
public String handleArgumentType(final String argumentType) {
switch (argumentType) {
case "minecraft:resource":
return "minecraft:resource_key";
case "minecraft:resource_or_tag":
return "minecraft:resource_or_tag_key";
case "minecraft:entity_summon":
case "minecraft:item_enchantment":
case "minecraft:mob_effect":
return "minecraft:resource";
}
return argumentType;
}
};
commandRewriter.registerDeclareCommands1_19(ClientboundPackets1_19_1.DECLARE_COMMANDS);
registerClientbound(ClientboundPackets1_19_1.SERVER_DATA, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.OPTIONAL_COMPONENT); // Motd
map(Type.OPTIONAL_STRING); // Encoded icon
read(Type.BOOLEAN); // Remove previews chat
}
});
// Aaaaand once more
registerClientbound(ClientboundPackets1_19_1.PLAYER_CHAT, ClientboundPackets1_19_3.DISGUISED_CHAT, new PacketRemapper() {
@Override
public void registerMap() {
read(Type.OPTIONAL_BYTE_ARRAY_PRIMITIVE); // Previous signature
handler(wrapper -> {
final PlayerMessageSignature signature = wrapper.read(Type.PLAYER_MESSAGE_SIGNATURE);
// Store message signature for last seen
if (!signature.uuid().equals(ZERO_UUID) && signature.signatureBytes().length != 0) {
final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class);
messagesStorage.add(signature);
if (messagesStorage.tickUnacknowledged() > 64) {
messagesStorage.resetUnacknowledgedCount();
// Send chat acknowledgement
final PacketWrapper chatAckPacket = wrapper.create(ServerboundPackets1_19_1.CHAT_ACK);
chatAckPacket.write(Type.PLAYER_MESSAGE_SIGNATURE_ARRAY, messagesStorage.lastSignatures());
chatAckPacket.write(Type.OPTIONAL_PLAYER_MESSAGE_SIGNATURE, null);
chatAckPacket.sendToServer(Protocol1_19_3To1_19_1.class);
}
}
final String plainMessage = wrapper.read(Type.STRING);
JsonElement decoratedMessage = wrapper.read(Type.OPTIONAL_COMPONENT);
wrapper.read(Type.LONG); // Timestamp
wrapper.read(Type.LONG); // Salt
wrapper.read(Type.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen
final JsonElement unsignedMessage = wrapper.read(Type.OPTIONAL_COMPONENT);
if (unsignedMessage != null) {
decoratedMessage = unsignedMessage;
}
if (decoratedMessage == null) {
decoratedMessage = GsonComponentSerializer.gson().serializeToTree(Component.text(plainMessage));
}
final int filterMaskType = wrapper.read(Type.VAR_INT);
if (filterMaskType == 2) { // Partially filtered
wrapper.read(Type.LONG_ARRAY_PRIMITIVE); // Mask
}
wrapper.write(Type.COMPONENT, decoratedMessage);
// Keep chat type at the end
});
}
});
registerServerbound(ServerboundPackets1_19_3.CHAT_COMMAND, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // Command
map(Type.LONG); // Timestamp
map(Type.LONG); // Salt
handler(wrapper -> {
final int signatures = wrapper.read(Type.VAR_INT);
wrapper.write(Type.VAR_INT, 0);
for (int i = 0; i < signatures; i++) {
wrapper.read(Type.STRING); // Argument name
wrapper.read(OPTIONAL_MESSAGE_SIGNATURE_BYTES_TYPE); // Signature
}
wrapper.write(Type.BOOLEAN, false); // No signed preview
final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class);
messagesStorage.resetUnacknowledgedCount();
wrapper.write(Type.PLAYER_MESSAGE_SIGNATURE_ARRAY, messagesStorage.lastSignatures());
wrapper.write(Type.OPTIONAL_PLAYER_MESSAGE_SIGNATURE, null); // No last unacknowledged
});
read(Type.VAR_INT); // Offset
read(new BitSetType(20)); // Acknowledged
}
});
registerServerbound(ServerboundPackets1_19_3.CHAT_MESSAGE, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // Command
map(Type.LONG); // Timestamp
// Salt
read(Type.LONG);
create(Type.LONG, 0L);
handler(wrapper -> {
// Remove signature
final byte[] signature = wrapper.read(OPTIONAL_MESSAGE_SIGNATURE_BYTES_TYPE);
wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, EMPTY_BYTES);
wrapper.write(Type.BOOLEAN, false); // No signed preview
final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class);
messagesStorage.resetUnacknowledgedCount();
wrapper.write(Type.PLAYER_MESSAGE_SIGNATURE_ARRAY, messagesStorage.lastSignatures());
wrapper.write(Type.OPTIONAL_PLAYER_MESSAGE_SIGNATURE, null); // No last unacknowledged
});
read(Type.VAR_INT); // Offset
read(new BitSetType(20)); // Acknowledged
}
});
// Remove the key once again
registerServerbound(State.LOGIN, ServerboundLoginPackets.HELLO.getId(), ServerboundLoginPackets.HELLO.getId(), new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // Name
create(Type.OPTIONAL_PROFILE_KEY, null);
}
});
registerServerbound(State.LOGIN, ServerboundLoginPackets.ENCRYPTION_KEY.getId(), ServerboundLoginPackets.ENCRYPTION_KEY.getId(), new PacketRemapper() {
@Override
public void registerMap() {
map(Type.BYTE_ARRAY_PRIMITIVE); // Keys
create(Type.BOOLEAN, true); // Is nonce
map(Type.BYTE_ARRAY_PRIMITIVE); // Encrypted challenge
}
});
cancelServerbound(ServerboundPackets1_19_3.CHAT_SESSION_UPDATE);
cancelClientbound(ClientboundPackets1_19_1.DELETE_CHAT_MESSAGE);
cancelClientbound(ClientboundPackets1_19_1.PLAYER_CHAT_HEADER);
cancelClientbound(ClientboundPackets1_19_1.CHAT_PREVIEW);
cancelClientbound(ClientboundPackets1_19_1.SET_DISPLAY_CHAT_PREVIEW);
cancelServerbound(ServerboundPackets1_19_3.CHAT_ACK);
}
@Override
protected void onMappingDataLoaded() {
entityRewriter.onMappingDataLoaded();
Types1_19_3.PARTICLE.filler(this)
.reader("block", ParticleType.Readers.BLOCK)
.reader("block_marker", ParticleType.Readers.BLOCK)
.reader("dust", ParticleType.Readers.DUST)
.reader("falling_dust", ParticleType.Readers.BLOCK)
.reader("dust_color_transition", ParticleType.Readers.DUST_TRANSITION)
.reader("item", ParticleType.Readers.VAR_INT_ITEM)
.reader("vibration", ParticleType.Readers.VIBRATION)
.reader("sculk_charge", ParticleType.Readers.SCULK_CHARGE)
.reader("shriek", ParticleType.Readers.SHRIEK);
Entity1_19_3Types.initialize(this);
}
@Override
public void init(final UserConnection user) {
user.put(new ReceivedMessagesStorage());
addEntityTracker(user, new EntityTrackerBase(user, Entity1_19_3Types.PLAYER));
}
@Override
public MappingData getMappingData() {
return MAPPINGS;
}
@Override
public EntityRewriter getEntityRewriter() {
return entityRewriter;
}
@Override
public ItemRewriter getItemRewriter() {
return itemRewriter;
}
}
@@ -0,0 +1,85 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
public enum ServerboundPackets1_19_3 implements ServerboundPacketType {
TELEPORT_CONFIRM, // 0x00
QUERY_BLOCK_NBT, // 0x01
SET_DIFFICULTY, // 0x02
CHAT_ACK, // 0x03
CHAT_COMMAND, // 0x04
CHAT_MESSAGE, // 0x05
CLIENT_STATUS, // 0x06
CLIENT_SETTINGS, // 0x07
TAB_COMPLETE, // 0x08
CLICK_WINDOW_BUTTON, // 0x09
CLICK_WINDOW, // 0x0A
CLOSE_WINDOW, // 0x0B
PLUGIN_MESSAGE, // 0x0C
EDIT_BOOK, // 0x0D
ENTITY_NBT_REQUEST, // 0x0E
INTERACT_ENTITY, // 0x0F
GENERATE_JIGSAW, // 0x10
KEEP_ALIVE, // 0x11
LOCK_DIFFICULTY, // 0x12
PLAYER_POSITION, // 0x13
PLAYER_POSITION_AND_ROTATION, // 0x14
PLAYER_ROTATION, // 0x15
PLAYER_MOVEMENT, // 0x16
VEHICLE_MOVE, // 0x17
STEER_BOAT, // 0x18
PICK_ITEM, // 0x19
CRAFT_RECIPE_REQUEST, // 0x1A
PLAYER_ABILITIES, // 0x1B
PLAYER_DIGGING, // 0x1C
ENTITY_ACTION, // 0x1D
STEER_VEHICLE, // 0x1E
PONG, // 0x1F
CHAT_SESSION_UPDATE,
RECIPE_BOOK_DATA, // 0x20
SEEN_RECIPE, // 0x21
RENAME_ITEM, // 0x22
RESOURCE_PACK_STATUS, // 0x23
ADVANCEMENT_TAB, // 0x24
SELECT_TRADE, // 0x25
SET_BEACON_EFFECT, // 0x26
HELD_ITEM_CHANGE, // 0x27
UPDATE_COMMAND_BLOCK, // 0x28
UPDATE_COMMAND_BLOCK_MINECART, // 0x29
CREATIVE_INVENTORY_ACTION, // 0x2A
UPDATE_JIGSAW_BLOCK, // 0x2B
UPDATE_STRUCTURE_BLOCK, // 0x2C
UPDATE_SIGN, // 0x2D
ANIMATION, // 0x2E
SPECTATE, // 0x2F
PLAYER_BLOCK_PLACEMENT, // 0x30
USE_ITEM; // 0x31
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
}
@@ -0,0 +1,89 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.data;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.data.RecipeRewriter1_16;
public class RecipeRewriter1_19_3 extends RecipeRewriter1_16 {
public RecipeRewriter1_19_3(final Protocol protocol) {
super(protocol);
recipeHandlers.put("crafting_special_armordye", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_bookcloning", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_mapcloning", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_mapextending", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_firework_rocket", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_firework_star", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_firework_star_fade", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_tippedarrow", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_bannerduplicate", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_shielddecoration", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_shulkerboxcoloring", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_suspiciousstew", this::handleSimpleRecipe);
recipeHandlers.put("crafting_special_repairitem", this::handleSimpleRecipe);
}
@Override
public void handleCraftingShapeless(final PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
wrapper.passthrough(Type.VAR_INT); // Crafting book category
final int ingredients = wrapper.passthrough(Type.VAR_INT);
for (int j = 0; j < ingredients; j++) {
final Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT); // Ingredients
for (final Item item : items) {
rewrite(item);
}
}
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
}
@Override
public void handleCraftingShaped(final PacketWrapper wrapper) throws Exception {
final int ingredients = wrapper.passthrough(Type.VAR_INT) * wrapper.passthrough(Type.VAR_INT);
wrapper.passthrough(Type.STRING); // Group
wrapper.passthrough(Type.VAR_INT); // Crafting book category
for (int j = 0; j < ingredients; j++) {
final Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT); // Ingredients
for (final Item item : items) {
rewrite(item);
}
}
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
}
@Override
public void handleSmelting(final PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
wrapper.passthrough(Type.VAR_INT); // Crafting book category
final Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT); // Ingredients
for (final Item item : items) {
rewrite(item);
}
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
wrapper.passthrough(Type.FLOAT); // EXP
wrapper.passthrough(Type.VAR_INT); // Cooking time
}
public void handleSimpleRecipe(final PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.VAR_INT); // Crafting book category
}
}
@@ -0,0 +1,182 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.packets;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_19_3Types;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.BitSetType;
import com.viaversion.viaversion.api.type.types.version.Types1_19;
import com.viaversion.viaversion.api.type.types.version.Types1_19_3;
import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.ClientboundPackets1_19_1;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.ClientboundPackets1_19_3;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.Protocol1_19_3To1_19_1;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import java.util.BitSet;
import java.util.UUID;
public final class EntityPackets extends EntityRewriter<Protocol1_19_3To1_19_1> {
private static final BitSetType PROFILE_ACTIONS_ENUM_TYPE = new BitSetType(6);
public EntityPackets(final Protocol1_19_3To1_19_1 protocol) {
super(protocol);
}
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_19_1.SPAWN_ENTITY, Entity1_19_3Types.FALLING_BLOCK);
registerMetadataRewriter(ClientboundPackets1_19_1.ENTITY_METADATA, Types1_19.METADATA_LIST, Types1_19_3.METADATA_LIST);
registerRemoveEntities(ClientboundPackets1_19_1.REMOVE_ENTITIES);
protocol.registerClientbound(ClientboundPackets1_19_1.JOIN_GAME, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // Entity id
map(Type.BOOLEAN); // Hardcore
map(Type.UNSIGNED_BYTE); // Gamemode
map(Type.BYTE); // Previous Gamemode
map(Type.STRING_ARRAY); // World List
map(Type.NBT); // Dimension registry
map(Type.STRING); // Dimension key
map(Type.STRING); // World
handler(dimensionDataHandler());
handler(biomeSizeTracker());
handler(worldDataTrackerHandlerByKey());
handler(wrapper -> {
// Also enable vanilla features
final PacketWrapper enableFeaturesPacket = wrapper.create(ClientboundPackets1_19_3.UPDATE_ENABLED_FEATURES);
enableFeaturesPacket.write(Type.VAR_INT, 1);
enableFeaturesPacket.write(Type.STRING, "minecraft:vanilla");
enableFeaturesPacket.send(Protocol1_19_3To1_19_1.class);
});
}
});
protocol.registerClientbound(ClientboundPackets1_19_1.RESPAWN, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // Dimension
map(Type.STRING); // World
map(Type.LONG); // Seed
map(Type.UNSIGNED_BYTE); // Gamemode
map(Type.BYTE); // Previous gamemode
map(Type.BOOLEAN); // Debug
map(Type.BOOLEAN); // Flat
handler(worldDataTrackerHandlerByKey());
handler(wrapper -> {
final boolean keepAttributes = wrapper.read(Type.BOOLEAN);
byte keepDataMask = 0x02; // Always keep entity data
if (keepAttributes) {
keepDataMask |= 0x01;
}
wrapper.write(Type.BYTE, keepDataMask);
});
}
});
protocol.registerClientbound(ClientboundPackets1_19_1.PLAYER_INFO, ClientboundPackets1_19_3.PLAYER_INFO_UPDATE, new PacketRemapper() {
@Override
public void registerMap() {
handler(wrapper -> {
final int action = wrapper.read(Type.VAR_INT);
if (action == 4) { // Remove player
// Write into new packet type
final int entries = wrapper.read(Type.VAR_INT);
final UUID[] uuidsToRemove = new UUID[entries];
for (int i = 0; i < entries; i++) {
uuidsToRemove[i] = wrapper.read(Type.UUID);
}
wrapper.write(Type.UUID_ARRAY, uuidsToRemove);
wrapper.setPacketType(ClientboundPackets1_19_3.PLAYER_INFO_REMOVE);
return;
}
final BitSet set = new BitSet(6);
if (action == 0) {
// Includes add player, profile key, gamemode, listed status, latency, and display name
set.set(0, 6);
} else {
// Update listed added at 3, initialize chat added at index 1
set.set(action == 1 ? action + 1 : action + 2);
}
wrapper.write(PROFILE_ACTIONS_ENUM_TYPE, set);
final int entries = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < entries; i++) {
wrapper.passthrough(Type.UUID); // UUID
if (action == 0) { // Add player
wrapper.passthrough(Type.STRING); // Player Name
final int properties = wrapper.passthrough(Type.VAR_INT);
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Type.STRING); // Name
wrapper.passthrough(Type.STRING); // Value
wrapper.passthrough(Type.OPTIONAL_STRING); // Signature
}
final int gamemode = wrapper.read(Type.VAR_INT);
final int ping = wrapper.read(Type.VAR_INT);
final JsonElement displayName = wrapper.read(Type.OPTIONAL_COMPONENT);
wrapper.read(Type.OPTIONAL_PROFILE_KEY);
wrapper.write(Type.BOOLEAN, false); // No chat session data
wrapper.write(Type.VAR_INT, gamemode);
wrapper.write(Type.BOOLEAN, true); // Also update listed
wrapper.write(Type.VAR_INT, ping);
wrapper.write(Type.OPTIONAL_COMPONENT, displayName);
} else if (action == 1 || action == 2) { // Update gamemode/update latency
wrapper.passthrough(Type.VAR_INT);
} else if (action == 3) { // Update display name
wrapper.passthrough(Type.OPTIONAL_COMPONENT);
}
}
});
}
});
}
@Override
protected void registerRewrites() {
filter().handler((event, meta) -> {
final int id = meta.metaType().typeId();
meta.setMetaType(Types1_19_3.META_TYPES.byId(id >= 2 ? id + 1 : id)); // long added
});
registerMetaTypeHandler(Types1_19_3.META_TYPES.itemType, Types1_19_3.META_TYPES.blockStateType, Types1_19_3.META_TYPES.particleType);
filter().filterFamily(Entity1_19_3Types.MINECART_ABSTRACT).index(11).handler((event, meta) -> {
// Convert to new block id
final int data = (int) meta.getValue();
meta.setValue(protocol.getMappingData().getNewBlockStateId(data));
});
}
@Override
public void onMappingDataLoaded() {
mapTypes();
}
@Override
public EntityType typeFromId(final int type) {
return Entity1_19_3Types.getTypeFromId(type);
}
}
@@ -0,0 +1,143 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.packets;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.data.RecipeRewriter1_16;
import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.types.Chunk1_18Type;
import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.ClientboundPackets1_19_1;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.Protocol1_19_3To1_19_1;
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.ServerboundPackets1_19_3;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.ItemRewriter;
public final class InventoryPackets extends ItemRewriter<Protocol1_19_3To1_19_1> {
private static final int MISC_CRAFTING_BOOK_CATEGORY = 0;
public InventoryPackets(final Protocol1_19_3To1_19_1 protocol) {
super(protocol);
}
@Override
public void registerPackets() {
final BlockRewriter blockRewriter = new BlockRewriter(protocol, Type.POSITION1_14);
blockRewriter.registerBlockAction(ClientboundPackets1_19_1.BLOCK_ACTION);
blockRewriter.registerBlockChange(ClientboundPackets1_19_1.BLOCK_CHANGE);
blockRewriter.registerVarLongMultiBlockChange(ClientboundPackets1_19_1.MULTI_BLOCK_CHANGE);
blockRewriter.registerEffect(ClientboundPackets1_19_1.EFFECT, 1010, 2001);
blockRewriter.registerChunkData1_19(ClientboundPackets1_19_1.CHUNK_DATA, Chunk1_18Type::new);
blockRewriter.registerBlockEntityData(ClientboundPackets1_19_1.BLOCK_ENTITY_DATA);
registerSetCooldown(ClientboundPackets1_19_1.COOLDOWN);
registerWindowItems1_17_1(ClientboundPackets1_19_1.WINDOW_ITEMS);
registerSetSlot1_17_1(ClientboundPackets1_19_1.SET_SLOT);
registerAdvancements(ClientboundPackets1_19_1.ADVANCEMENTS, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_19_1.ENTITY_EQUIPMENT);
registerClickWindow1_17_1(ServerboundPackets1_19_3.CLICK_WINDOW);
registerTradeList1_19(ClientboundPackets1_19_1.TRADE_LIST);
registerCreativeInvAction(ServerboundPackets1_19_3.CREATIVE_INVENTORY_ACTION, Type.FLAT_VAR_INT_ITEM);
registerWindowPropertyEnchantmentHandler(ClientboundPackets1_19_1.WINDOW_PROPERTY);
registerSpawnParticle1_19(ClientboundPackets1_19_1.SPAWN_PARTICLE);
final RecipeRewriter1_16 recipeRewriter = new RecipeRewriter1_16(protocol);
protocol.registerClientbound(ClientboundPackets1_19_1.DECLARE_RECIPES, new PacketRemapper() {
@Override
public void registerMap() {
handler(wrapper -> {
final int size = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < size; i++) {
final String type = wrapper.passthrough(Type.STRING).replace("minecraft:", "");
wrapper.passthrough(Type.STRING); // Recipe Identifier
switch (type) {
case "crafting_shapeless": {
wrapper.passthrough(Type.STRING); // Group
wrapper.write(Type.VAR_INT, MISC_CRAFTING_BOOK_CATEGORY);
final int ingredients = wrapper.passthrough(Type.VAR_INT);
for (int j = 0; j < ingredients; j++) {
final Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT); // Ingredients
for (final Item item : items) {
handleItemToClient(item);
}
}
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
break;
}
case "crafting_shaped": {
final int ingredients = wrapper.passthrough(Type.VAR_INT) * wrapper.passthrough(Type.VAR_INT);
wrapper.passthrough(Type.STRING); // Group
wrapper.write(Type.VAR_INT, MISC_CRAFTING_BOOK_CATEGORY);
for (int j = 0; j < ingredients; j++) {
final Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT); // Ingredients
for (final Item item : items) {
handleItemToClient(item);
}
}
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
break;
}
case "smelting":
case "campfire_cooking":
case "blasting":
case "smoking":
wrapper.passthrough(Type.STRING); // Group
wrapper.write(Type.VAR_INT, MISC_CRAFTING_BOOK_CATEGORY);
final Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT); // Ingredients
for (final Item item : items) {
handleItemToClient(item);
}
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
wrapper.passthrough(Type.FLOAT); // EXP
wrapper.passthrough(Type.VAR_INT); // Cooking time
break;
case "crafting_special_armordye":
case "crafting_special_bookcloning":
case "crafting_special_mapcloning":
case "crafting_special_mapextending":
case "crafting_special_firework_rocket":
case "crafting_special_firework_star":
case "crafting_special_firework_star_fade":
case "crafting_special_tippedarrow":
case "crafting_special_bannerduplicate":
case "crafting_special_shielddecoration":
case "crafting_special_shulkerboxcoloring":
case "crafting_special_suspiciousstew":
case "crafting_special_repairitem":
wrapper.write(Type.VAR_INT, MISC_CRAFTING_BOOK_CATEGORY);
break;
default:
recipeRewriter.handle(wrapper, type);
break;
}
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_19_1.EXPLOSION, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.FLOAT, Type.DOUBLE); // X
map(Type.FLOAT, Type.DOUBLE); // Y
map(Type.FLOAT, Type.DOUBLE); // Z
}
});
}
}
@@ -0,0 +1,64 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;
import java.util.Arrays;
public final class ReceivedMessagesStorage implements StorableObject {
private final PlayerMessageSignature[] signatures = new PlayerMessageSignature[5];
private PlayerMessageSignature lastSignature;
private int size;
private int unacknowledged;
public boolean add(final PlayerMessageSignature signature) {
if (signature.equals(this.lastSignature)) {
return false;
}
this.lastSignature = signature;
PlayerMessageSignature toPush = signature;
for (int i = 0; i < this.size; ++i) {
final PlayerMessageSignature entry = this.signatures[i];
this.signatures[i] = toPush;
toPush = entry;
if (entry.uuid().equals(signature.uuid())) {
return true;
}
}
if (this.size < this.signatures.length) {
this.signatures[this.size++] = toPush;
}
return true;
}
public PlayerMessageSignature[] lastSignatures() {
return Arrays.copyOf(this.signatures, size);
}
public int tickUnacknowledged() {
return unacknowledged++;
}
public void resetUnacknowledgedCount() {
unacknowledged = 0;
}
}
@@ -64,6 +64,10 @@ public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPack
super(ClientboundPackets1_18.class, ClientboundPackets1_19.class, ServerboundPackets1_17.class, ServerboundPackets1_19.class);
}
public static boolean isTextComponentNull(final JsonElement element) {
return element == null || element.isJsonNull() || (element.isJsonArray() && element.getAsJsonArray().size() == 0);
}
@Override
protected void registerPackets() {
final TagRewriter tagRewriter = new TagRewriter(this);
@@ -86,7 +90,7 @@ public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPack
map(Type.INT); // Z
map(Type.FLOAT); // Volume
map(Type.FLOAT); // Pitch
create(Type.LONG, randomLong()); // Seed
handler(wrapper -> wrapper.write(Type.LONG, randomLong())); // Seed
handler(soundRewriter.getSoundHandler());
}
});
@@ -98,7 +102,7 @@ public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPack
map(Type.VAR_INT); // Entity id
map(Type.FLOAT); // Volume
map(Type.FLOAT); // Pitch
create(Type.LONG, randomLong()); // Seed
handler(wrapper -> wrapper.write(Type.LONG, randomLong())); // Seed
handler(soundRewriter.getSoundHandler());
}
});
@@ -112,7 +116,7 @@ public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPack
map(Type.INT); // Z
map(Type.FLOAT); // Volume
map(Type.FLOAT); // Pitch
create(Type.LONG, randomLong()); // Seed
handler(wrapper -> wrapper.write(Type.LONG, randomLong())); // Seed
}
});
@@ -120,7 +124,7 @@ public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPack
final PacketHandler titleHandler = wrapper -> {
final JsonElement component = wrapper.read(Type.COMPONENT);
if (!component.isJsonNull()) {
if (!isTextComponentNull(component)) {
wrapper.write(Type.COMPONENT, component);
} else {
wrapper.write(Type.COMPONENT, GsonComponentSerializer.gson().serializeToTree(Component.empty()));
@@ -22,6 +22,7 @@ import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.google.common.collect.Maps;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.entity.DimensionData;
import com.viaversion.viaversion.api.minecraft.Position;
@@ -75,7 +76,7 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
" ]\n" +
" }\n" +
"}";
private static final CompoundTag CHAT_REGISTRY;
public static final CompoundTag CHAT_REGISTRY;
static {
try {
@@ -151,7 +152,7 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
final PacketWrapper metaPacket = wrapper.create(ClientboundPackets1_19.ENTITY_METADATA);
metaPacket.write(Type.VAR_INT, wrapper.get(Type.VAR_INT, 0)); // Entity id
final List<Metadata> metadata = new ArrayList<>();
metadata.add(new Metadata(8, Types1_19.META_TYPES.paintingVariantType, protocol.getMappingData().getPaintingMappings().getNewId(motive)));
metadata.add(new Metadata(8, Types1_19.META_TYPES.paintingVariantType, protocol.getMappingData().getPaintingMappings().getNewIdOrDefault(motive, 0)));
metaPacket.write(Types1_19.METADATA_LIST, metadata);
metaPacket.send(Protocol1_19To1_18_2.class);
});
@@ -280,15 +281,16 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Type.STRING); // Name
wrapper.passthrough(Type.STRING); // Value
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.passthrough(Type.STRING); // Signature
}
wrapper.passthrough(Type.OPTIONAL_STRING); // Signature
}
wrapper.passthrough(Type.VAR_INT); // Gamemode
wrapper.passthrough(Type.VAR_INT); // Ping
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.passthrough(Type.COMPONENT); // Display name
final JsonElement displayName = wrapper.read(Type.OPTIONAL_COMPONENT); // Display name
if (!Protocol1_19To1_18_2.isTextComponentNull(displayName)) {
wrapper.write(Type.OPTIONAL_COMPONENT, displayName);
} else {
wrapper.write(Type.OPTIONAL_COMPONENT, null);
}
// No public profile signature
@@ -296,8 +298,11 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
} else if (action == 1 || action == 2) { // Update gamemode/update latency
wrapper.passthrough(Type.VAR_INT);
} else if (action == 3) { // Update display name
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.passthrough(Type.COMPONENT);
final JsonElement displayName = wrapper.read(Type.OPTIONAL_COMPONENT); // Display name
if (!Protocol1_19To1_18_2.isTextComponentNull(displayName)) {
wrapper.write(Type.OPTIONAL_COMPONENT, displayName);
} else {
wrapper.write(Type.OPTIONAL_COMPONENT, null);
}
}
}
@@ -37,10 +37,10 @@ public final class InventoryPackets extends ItemRewriter<Protocol1_19To1_18_2> {
@Override
public void registerPackets() {
registerSetCooldown(ClientboundPackets1_18.COOLDOWN);
registerWindowItems1_17_1(ClientboundPackets1_18.WINDOW_ITEMS, Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT, Type.FLAT_VAR_INT_ITEM);
registerSetSlot1_17_1(ClientboundPackets1_18.SET_SLOT, Type.FLAT_VAR_INT_ITEM);
registerWindowItems1_17_1(ClientboundPackets1_18.WINDOW_ITEMS);
registerSetSlot1_17_1(ClientboundPackets1_18.SET_SLOT);
registerAdvancements(ClientboundPackets1_18.ADVANCEMENTS, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_18.ENTITY_EQUIPMENT, Type.FLAT_VAR_INT_ITEM);
registerEntityEquipmentArray(ClientboundPackets1_18.ENTITY_EQUIPMENT);
protocol.registerClientbound(ClientboundPackets1_18.SPAWN_PARTICLE, new PacketRemapper() {
@Override
public void registerMap() {
@@ -58,7 +58,7 @@ public final class InventoryPackets extends ItemRewriter<Protocol1_19To1_18_2> {
}
});
registerClickWindow1_17_1(ServerboundPackets1_19.CLICK_WINDOW, Type.FLAT_VAR_INT_ITEM);
registerClickWindow1_17_1(ServerboundPackets1_19.CLICK_WINDOW);
registerCreativeInvAction(ServerboundPackets1_19.CREATIVE_INVENTORY_ACTION, Type.FLAT_VAR_INT_ITEM);
registerWindowPropertyEnchantmentHandler(ClientboundPackets1_18.WINDOW_PROPERTY);
@@ -18,6 +18,8 @@
package com.viaversion.viaversion.protocols.protocol1_9_1_2to1_9_3_4;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
@@ -63,7 +65,9 @@ public class Protocol1_9_1_2To1_9_3_4 extends AbstractProtocol<ClientboundPacket
wrapper.write(Type.POSITION, position); // Position
for (int i = 1; i < 5; i++) {
// Should technically be written as COMPONENT, but left as String for simplification/to remove redundant wrapping for VR
wrapper.write(Type.STRING, (String) tag.get("Text" + i).getValue()); // Sign line
Tag textTag = tag.get("Text" + i);
String line = textTag instanceof StringTag ? ((StringTag) textTag).getValue() : "";
wrapper.write(Type.STRING, line); // Sign line
}
}
}
@@ -25,6 +25,8 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
@@ -104,18 +106,20 @@ public class Protocol1_9_3To1_9_1_2 extends AbstractProtocol<ClientboundPackets1
wrapper.write(new Chunk1_9_3_4Type(clientWorld), chunk);
List<CompoundTag> tags = chunk.getBlockEntities();
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
for (int s = 0; s < chunk.getSections().length; s++) {
ChunkSection section = chunk.getSections()[s];
if (section == null) continue;
DataPalette blocks = section.palette(PaletteType.BLOCKS);
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getBlockWithoutData(x, y, z);
if (FakeTileEntity.isTileEntity(block)) {
tags.add(FakeTileEntity.createTileEntity(x + (chunk.getX() << 4), y + (i << 4), z + (chunk.getZ() << 4), block));
}
}
for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx) >> 4;
if (FakeTileEntity.isTileEntity(id)) {
tags.add(FakeTileEntity.createTileEntity(
ChunkSection.xFromIndex(idx) + (chunk.getX() << 4),
ChunkSection.yFromIndex(idx) + (s << 4),
ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4),
id
));
}
}
}
@@ -61,12 +61,12 @@ public class EntityPackets {
map(Type.INT); // 0 - Entity ID
map(Type.INT); // 1 - Vehicle
// Leash boolean is removed in new versions
map(Type.BOOLEAN, new ValueTransformer<Boolean, Void>(Type.NOTHING) {
// Leash state is removed in new versions
map(Type.UNSIGNED_BYTE, new ValueTransformer<Short, Void>(Type.NOTHING) {
@Override
public Void transform(PacketWrapper wrapper, Boolean inputValue) throws Exception {
public Void transform(PacketWrapper wrapper, Short inputValue) throws Exception {
EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
if (!inputValue) {
if (inputValue == 0) {
int passenger = wrapper.get(Type.INT, 0);
int vehicle = wrapper.get(Type.INT, 1);
@@ -241,25 +241,20 @@ public class PlayerPackets {
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Type.STRING); // name
wrapper.passthrough(Type.STRING); // value
boolean isSigned = wrapper.passthrough(Type.BOOLEAN);
if (isSigned) {
wrapper.passthrough(Type.STRING); // signature
}
wrapper.passthrough(Type.OPTIONAL_STRING); // signature
}
wrapper.passthrough(Type.VAR_INT); // gamemode
wrapper.passthrough(Type.VAR_INT); // ping
boolean hasDisplayName = wrapper.passthrough(Type.BOOLEAN);
if (hasDisplayName) {
Protocol1_9To1_8.FIX_JSON.write(wrapper, wrapper.read(Type.STRING)); // display name
}
String displayName = wrapper.read(Type.OPTIONAL_STRING);
wrapper.write(Type.OPTIONAL_COMPONENT, displayName != null ?
Protocol1_9To1_8.FIX_JSON.transform(wrapper, displayName) : null);
} else if ((action == 1) || (action == 2)) { // update gamemode || update latency
wrapper.passthrough(Type.VAR_INT);
} else if (action == 3) { // update display name
boolean hasDisplayName = wrapper.passthrough(Type.BOOLEAN);
if (hasDisplayName) {
Protocol1_9To1_8.FIX_JSON.write(wrapper, wrapper.read(Type.STRING)); // display name
}
String displayName = wrapper.read(Type.OPTIONAL_STRING);
wrapper.write(Type.OPTIONAL_COMPONENT, displayName != null ?
Protocol1_9To1_8.FIX_JSON.transform(wrapper, displayName) : null);
} else if (action == 4) { // remove player
// no fields
}
@@ -17,12 +17,23 @@
*/
package com.viaversion.viaversion.rewriter;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.data.Mappings;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.MathUtil;
import java.util.List;
public class BlockRewriter {
private final Protocol protocol;
@@ -121,4 +132,56 @@ public class BlockRewriter {
}
});
}
public void registerChunkData1_19(ClientboundPacketType packetType, ChunkTypeSupplier chunkTypeSupplier) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
handler(wrapper -> {
final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());
Preconditions.checkArgument(tracker.biomesSent() != 0, "Biome count not set");
Preconditions.checkArgument(tracker.currentWorldSectionHeight() != 0, "Section height not set");
final Type<Chunk> chunkType = chunkTypeSupplier.supply(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),
MathUtil.ceilLog2(tracker.biomesSent()));
final Chunk chunk = wrapper.passthrough(chunkType);
for (final ChunkSection section : chunk.getSections()) {
final DataPalette blockPalette = section.palette(PaletteType.BLOCKS);
for (int i = 0; i < blockPalette.size(); i++) {
final int id = blockPalette.idByIndex(i);
blockPalette.setIdByIndex(i, protocol.getMappingData().getNewBlockStateId(id));
}
}
final Mappings blockEntityMappings = protocol.getMappingData().getBlockEntityMappings();
if (blockEntityMappings != null) {
List<BlockEntity> blockEntities = chunk.blockEntities();
for (int i = 0; i < blockEntities.size(); i++) {
final BlockEntity blockEntity = blockEntities.get(i);
blockEntities.set(i, blockEntity.withTypeId(protocol.getMappingData().getBlockEntityMappings().getNewIdOrDefault(blockEntity.typeId(), blockEntity.typeId())));
}
}
});
}
});
}
public void registerBlockEntityData(ClientboundPacketType packetType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.POSITION1_14);
handler(wrapper -> {
final int blockEntityId = wrapper.read(Type.VAR_INT);
wrapper.write(Type.VAR_INT, protocol.getMappingData().getBlockEntityMappings().getNewIdOrDefault(blockEntityId, blockEntityId));
});
}
});
}
@FunctionalInterface
public interface ChunkTypeSupplier {
Type<Chunk> supply(int ySectionCount, int globalPaletteBlockBits, int globalPaletteBiomeBits);
}
}
@@ -17,6 +17,7 @@
*/
package com.viaversion.viaversion.rewriter;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
@@ -73,6 +74,12 @@ public class CommandRewriter {
this.parserHandlers.put("minecraft:resource_or_tag", wrapper -> {
wrapper.passthrough(Type.STRING); // Resource location/tag
});
this.parserHandlers.put("minecraft:resource_or_tag_key", wrapper -> {
wrapper.passthrough(Type.STRING); // Resource location
});
this.parserHandlers.put("minecraft:resource_key", wrapper -> {
wrapper.passthrough(Type.STRING); // Resource location/tag
});
}
public void handleArgument(PacketWrapper wrapper, String argumentType) throws Exception {
@@ -144,9 +151,8 @@ public class CommandRewriter {
int argumentTypeId = wrapper.read(Type.VAR_INT);
String argumentType = protocol.getMappingData().getArgumentTypeMappings().identifier(argumentTypeId);
String newArgumentType = handleArgumentType(argumentType);
if (newArgumentType != null) {
wrapper.write(Type.VAR_INT, protocol.getMappingData().getArgumentTypeMappings().mappedId(newArgumentType));
}
Preconditions.checkArgument(newArgumentType != null, "No mapping for argument type " + argumentType);
wrapper.write(Type.VAR_INT, protocol.getMappingData().getArgumentTypeMappings().mappedId(newArgumentType));
// Always call the handler using the previous name
handleArgument(wrapper, argumentType);
@@ -40,6 +40,7 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.rewriter.RewriterBase;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.Particle;
import com.viaversion.viaversion.data.entity.DimensionDataImpl;
import com.viaversion.viaversion.rewriter.meta.MetaFilter;
import com.viaversion.viaversion.rewriter.meta.MetaHandlerEvent;
import com.viaversion.viaversion.rewriter.meta.MetaHandlerEventImpl;
@@ -47,7 +48,9 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -446,10 +449,10 @@ public abstract class EntityRewriter<T extends Protocol> extends RewriterBase<T>
public PacketHandler worldDataTrackerHandlerByKey() {
return wrapper -> {
EntityTracker tracker = tracker(wrapper.user());
String key = wrapper.get(Type.STRING, 0);
DimensionData dimensionData = tracker.dimensionData(key);
String dimensionKey = wrapper.get(Type.STRING, 0);
DimensionData dimensionData = tracker.dimensionData(dimensionKey);
if (dimensionData == null) {
Via.getPlatform().getLogger().severe("Dimension data missing for dimension: " + key + ", falling back to overworld");
Via.getPlatform().getLogger().severe("Dimension data missing for dimension: " + dimensionKey + ", falling back to overworld");
dimensionData = tracker.dimensionData("minecraft:overworld");
Preconditions.checkNotNull(dimensionData, "Overworld data missing");
}
@@ -457,7 +460,7 @@ public abstract class EntityRewriter<T extends Protocol> extends RewriterBase<T>
tracker.setCurrentWorldSectionHeight(dimensionData.height() >> 4);
tracker.setCurrentMinY(dimensionData.minY());
String world = wrapper.get(Type.STRING, 0);
String world = wrapper.get(Type.STRING, 1);
if (tracker.currentWorld() != null && !tracker.currentWorld().equals(world)) {
tracker.clearEntities();
tracker.trackClientEntity();
@@ -475,6 +478,26 @@ public abstract class EntityRewriter<T extends Protocol> extends RewriterBase<T>
};
}
/**
* Returns a handler to cache dimension data, later used to get height values and other important info.
*
* @return handler to cache dimension data
*/
public PacketHandler dimensionDataHandler() {
return wrapper -> {
final CompoundTag tag = wrapper.get(Type.NBT, 0);
final ListTag dimensions = ((CompoundTag) tag.get("minecraft:dimension_type")).get("value");
final Map<String, DimensionData> dimensionDataMap = new HashMap<>(dimensions.size());
for (final Tag dimension : dimensions) {
final CompoundTag dimensionCompound = (CompoundTag) dimension;
final CompoundTag element = dimensionCompound.get("element");
final String name = (String) dimensionCompound.get("name").getValue();
dimensionDataMap.put(name, new DimensionDataImpl(element));
}
tracker(wrapper.user()).setDimensions(dimensionDataMap);
};
}
// ---------------------------------------------------------------------------
// Sub 1.14.1 methods
@@ -66,21 +66,19 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
});
}
public void registerWindowItems1_17_1(ClientboundPacketType packetType, Type<Item[]> itemsType, Type<Item> carriedItemType) {
public void registerWindowItems1_17_1(ClientboundPacketType packetType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
map(itemsType); // Items
map(carriedItemType); // Carried item
handler(wrapper -> {
Item[] items = wrapper.get(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT, 0);
Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT);
for (Item item : items) {
handleItemToClient(item);
}
handleItemToClient(wrapper.get(Type.FLAT_VAR_INT_ITEM, 0));
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Carried item
});
}
});
@@ -98,15 +96,15 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
});
}
public void registerSetSlot1_17_1(ClientboundPacketType packetType, Type<Item> type) {
public void registerSetSlot1_17_1(ClientboundPacketType packetType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
map(Type.SHORT); // Slot id
map(type); // Item
handler(itemToClientHandler(type));
map(Type.FLAT_VAR_INT_ITEM); // Item
handler(itemToClientHandler(Type.FLAT_VAR_INT_ITEM));
}
});
}
@@ -126,7 +124,7 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
}
// 1.16+
public void registerEntityEquipmentArray(ClientboundPacketType packetType, Type<Item> type) {
public void registerEntityEquipmentArray(ClientboundPacketType packetType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
@@ -137,7 +135,7 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
do {
slot = wrapper.passthrough(Type.BYTE);
// & 0x7F into an extra variable if slot is needed
handleItemToClient(wrapper.passthrough(type));
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM));
} while ((slot & 0xFFFFFF80) != 0);
});
}
@@ -172,7 +170,7 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
});
}
public void registerClickWindow1_17_1(ServerboundPacketType packetType, Type<Item> type) {
public void registerClickWindow1_17_1(ServerboundPacketType packetType) {
protocol.registerServerbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
@@ -187,11 +185,11 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
int length = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < length; i++) {
wrapper.passthrough(Type.SHORT); // Slot
handleItemToServer(wrapper.passthrough(type));
handleItemToServer(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM));
}
// Carried item
handleItemToServer(wrapper.passthrough(type));
handleItemToServer(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM));
});
}
});
@@ -210,7 +208,7 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
}
// 1.14.4+
public void registerTradeList(ClientboundPacketType packetType, Type<Item> type) {
public void registerTradeList(ClientboundPacketType packetType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
@@ -218,11 +216,11 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
wrapper.passthrough(Type.VAR_INT);
int size = wrapper.passthrough(Type.UNSIGNED_BYTE);
for (int i = 0; i < size; i++) {
handleItemToClient(wrapper.passthrough(type)); // Input
handleItemToClient(wrapper.passthrough(type)); // Output
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Input
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Output
if (wrapper.passthrough(Type.BOOLEAN)) { // Has second item
handleItemToClient(wrapper.passthrough(type)); // Second Item
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Second Item
}
wrapper.passthrough(Type.BOOLEAN); // Trade disabled
@@ -240,7 +238,7 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
});
}
public void registerTradeList1_19(ClientboundPacketType packetType, Type<Item> type) {
public void registerTradeList1_19(ClientboundPacketType packetType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
@@ -248,9 +246,9 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
wrapper.passthrough(Type.VAR_INT); // Container id
int size = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < size; i++) {
handleItemToClient(wrapper.passthrough(type)); // Input
handleItemToClient(wrapper.passthrough(type)); // Output
handleItemToClient(wrapper.passthrough(type)); // Second Item
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Input
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Output
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Second Item
wrapper.passthrough(Type.BOOLEAN); // Trade disabled
wrapper.passthrough(Type.INT); // Number of tools uses
@@ -343,21 +341,21 @@ public abstract class ItemRewriter<T extends Protocol> extends RewriterBase<T> i
});
}
public void registerSpawnParticle1_19(ClientboundPacketType packetType, Type<Item> itemType, Type<?> coordType) {
public void registerSpawnParticle1_19(ClientboundPacketType packetType) {
protocol.registerClientbound(packetType, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Particle ID
map(Type.BOOLEAN); // 1 - Long Distance
map(coordType); // 2 - X
map(coordType); // 3 - Y
map(coordType); // 4 - Z
map(Type.DOUBLE); // 2 - X
map(Type.DOUBLE); // 3 - Y
map(Type.DOUBLE); // 4 - Z
map(Type.FLOAT); // 5 - Offset X
map(Type.FLOAT); // 6 - Offset Y
map(Type.FLOAT); // 7 - Offset Z
map(Type.FLOAT); // 8 - Particle Data
map(Type.INT); // 9 - Particle Count
handler(getSpawnParticleHandler(Type.VAR_INT, itemType));
handler(getSpawnParticleHandler(Type.VAR_INT, Type.FLAT_VAR_INT_ITEM));
}
});
}
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff. Show more