Compare commits

..
104 Commits
Author SHA1 Message Date
Nassim Jahnke a8acae2d5f 4.8.1 Release 2023-10-03 20:57:47 +10:00
Nassim Jahnke fdb7f47749 Properly throw the client information packet into the queue instead 2023-10-03 20:54:51 +10:00
Nassim Jahnke cabb95a3a4 Schedule send client information 2023-10-03 20:39:46 +10:00
Nassim Jahnke 0b93af88a3 Re-send client information when re-entering play state
Fixes #3442
2023-10-03 19:49:55 +10:00
Unoqwy 11cb113619 Fix disconnecting error for unknown mobs with metadata (#3443) 2023-10-03 09:46:02 +00:00
Nassim Jahnke 429e4f0bff Switch server state after sending game profile
Probably fixes #3453
2023-09-28 11:12:46 +10:00
Nassim Jahnke 3b0d1d961a Fix secondary effect reading in serverbound beacon
Fixes #3455
2023-09-28 09:34:26 +10:00
Nassim Jahnke b4720c723a Fix biome and chunk size preconditions 2023-09-27 14:35:20 +10:00
Nassim Jahnke 215cbc6310 Fix 1.20.2 custom potion effect handling
Fixes #3448
2023-09-27 11:30:35 +10:00
RK_01 f07ba9b527 Reduce delay for sequence acknowledgements (#3445) 2023-09-26 10:53:36 +10:00
Misode 5a20030ff3 [ci skip] Update Minecraft Wiki link to new domain after fork (#3446) 2023-09-26 10:52:03 +10:00
Nassim Jahnke 1b350c0c53 Try to fix ci 2023-09-26 10:40:44 +10:00
Nassim Jahnke 87db6fc75a [ci skip] Fix branch check 2023-09-26 00:17:33 +10:00
Nassim Jahnke 840770a350 Fix missing server state change 2023-09-25 20:11:29 +10:00
Nassim Jahnke 3997ea70f7 Fixup state splitting 2023-09-25 18:59:15 +10:00
Nassim Jahnke 854696abff Add separate client and server protocol state 2023-09-25 16:44:41 +10:00
Nassim Jahnke d92b9fdeaa Fix connection field search on 1.20.2 2023-09-25 09:57:55 +10:00
Nassim Jahnke 270d28f259 Don't cancel custom query answer 2023-09-25 08:56:26 +10:00
RK_01 ed1852ba40 Fix 1.20.2 -> 1.20.1 brand custom payloads properly (#3440) 2023-09-25 08:52:17 +10:00
riku6460 f91e6a83f9 Fix sign nbt on 1.20 client (#3436) 2023-09-24 13:02:31 +10:00
Nassim Jahnke 8a0e187621 Drop custom payloads with non-alphanumerical brands
For some reason the client errors only if it contains certain characters, needs further investigation. Mojang changed their string writing implementation, though the actual encoding does not seem to have changed, and this error does not occur anywhere else
2023-09-24 10:48:47 +10:00
Nassim Jahnke 0e8b5e8e98 Bump version to 4.8.1-SNAPSHOT 2023-09-24 10:47:43 +10:00
Nassim Jahnke e30dd63b3c [ci skip] Only publish releases from the main branch 2023-09-22 15:32:14 +10:00
Nassim Jahnke 489b28e520 4.8.0 Release 2023-09-21 10:34:20 +10:00
Nassim Jahnke d23e743550 Fix particle handling 2023-09-21 10:33:58 +10:00
Nassim Jahnke 16f89f2638 Don't re-enter config state if the dimension registry did not change 2023-09-19 21:14:45 +10:00
Nassim Jahnke fe5646a68c Re-send last resource pack on server switch 2023-09-19 11:54:03 +10:00
Nassim Jahnke 53aca791dd Fix reentering of the configuration state 2023-09-19 00:37:55 +10:00
Nassim Jahnke 1f0a4c26db 1.20.2-rc2 2023-09-19 00:07:43 +10:00
Nassim Jahnke 4c34ced240 Fix record handling due to no mappings
Fixes #3422
2023-09-16 13:28:15 +10:00
Nassim Jahnke 18c4f90cb0 Don't cancel system chat messages 2023-09-16 13:27:44 +10:00
Nassim Jahnke 9c90445c9c Release ConfigurationState buffers on disconnect
They're heap buffers, but still good practice
2023-09-16 13:27:23 +10:00
Nassim Jahnke 494e6ffc05 1.20.2-rc1 2023-09-16 10:19:49 +10:00
Nassim Jahnke a196dbc16b 1.20.2-pre4 2023-09-14 09:03:13 +10:00
Nassim Jahnke 8934bbdfc3 1.20.2-pre3 2023-09-12 23:06:54 +10:00
Myles 8316c59010 Merge pull request #3420 from Kichura/dev 2023-09-11 19:55:27 +01:00
Kichura 5ea1c1ec95 Use https instead of http for wiki.vg source. 2023-09-11 20:06:27 +02:00
Nassim Jahnke cc7c571a3c 1.20.2-pre2 2023-09-08 09:05:31 +10:00
Nassim Jahnke 06b4678b2d [ci skip] Add IDEA extension plugin 2023-09-06 11:20:32 +10:00
Nassim Jahnke a109118a97 1.20.2-pre1 2023-09-06 00:56:31 +10:00
Nassim Jahnke a135a1414c [ci skip] Update Gradle wrapper 2023-09-05 21:16:24 +10:00
Nassim Jahnke f50751ed48 [ci skip] Update blossom 2023-09-05 21:10:43 +10:00
Kichura 4b7998b4ea [ci skip] Update checkout to v4, Check for latest JDK 11 build (#3416) 2023-09-05 20:48:36 +10:00
Nassim Jahnke 6db1a2a614 Fix wrong packet type being used 2023-08-31 11:49:54 +10:00
Nassim Jahnke 625373827c Fix MetaTypes1_20_2 item type 2023-08-31 11:44:55 +10:00
Nassim Jahnke a9288ebd3a 23w35a 2023-08-31 00:42:47 +10:00
Nassim Jahnke 01bbb2c1c2 [ci skip] Update Hangar publish plugin 2023-08-26 20:04:06 +10:00
Nassim Jahnke 8dbc8e8eba Update mappings file for 23w33a 2023-08-18 01:28:55 +10:00
Nassim Jahnke 0c9f3aa080 23w33a 2023-08-18 01:27:00 +10:00
EnZaXD 00625be890 Move 1.9 backwards protocols to VB and move Chunk Types to the correct package (#3404) 2023-08-14 10:46:30 +10:00
Pablo Herrera e112f81ec8 Optimize memory allocation for data palette (#3402) 2023-08-14 10:40:10 +10:00
Nassim Jahnke 23f776a6ee Handle minecart blocks 2023-08-11 21:12:23 +10:00
Nassim Jahnke 0050872950 Update README 2023-08-11 21:12:03 +10:00
Nassim Jahnke 7e53f55509 Also publish dev branch builds to Hangar 2023-08-11 20:33:47 +10:00
Nassim Jahnke 1bf7962dc3 Call latestCommitHash only once 2023-08-11 16:24:27 +10:00
Nassim Jahnke 50176dc317 Include link to commit in autopublished changelog 2023-08-11 16:21:44 +10:00
Nassim Jahnke 9ed074d0d5 Handle changes block state ids 2023-08-11 15:52:53 +10:00
Nassim Jahnke 8bad8a10be Finish publishing action 2023-08-11 12:28:38 +10:00
Nassim Jahnke 34509e86c0 [ci skip] Add Velocity and Waterfall to publish config 2023-08-11 00:39:45 +10:00
Nassim Jahnke ef75bd567d [ci skip] GH already skips all actions with ci skip 2023-08-10 22:48:22 +10:00
Nassim Jahnke 7ed3e50e93 [ci skip] Skip commits with ci skip prefix 2023-08-10 22:46:15 +10:00
Nassim Jahnke a45eceaf1d Hello Hangar 😎 2023-08-10 22:18:23 +10:00
Nassim Jahnke 573cdfb3ac Merge remote-tracking branch 'origin/master' into dev 2023-08-10 21:49:26 +10:00
Nassim Jahnke 28787340d1 Setup Hangar and modrinth autopublishing 2023-08-10 21:29:03 +10:00
Nassim Jahnke c358245c0b Fix spawn tracker mapping data check 2023-08-10 17:14:01 +10:00
Nassim Jahnke cb073405b7 Effect ids start at 1 before 1.20.2 2023-08-10 17:08:36 +10:00
Nassim Jahnke f618f6a7d6 Actually write effect key 2023-08-10 13:51:15 +10:00
Nassim Jahnke c5e066e050 23w32a 2023-08-10 13:35:47 +10:00
riku6460 d954e1745d Fix itemArrayType of ItemRewriter on dev branch (#3398) 2023-08-09 07:38:24 +10:00
Nassim Jahnke 9c92ccea77 Finish up state switching 2023-08-06 15:40:15 +10:00
RK_01 2b9e0cec5c Fix maps with no map id set showing up as empty on 1.17+ (#3395) 2023-08-06 11:11:42 +10:00
RK_01 c1cd8bc03e Allow skylight to be set to null (#3396) 2023-08-06 11:08:52 +10:00
Nassim Jahnke 39efc5262e Add todo 2023-08-05 13:31:43 +10:00
Nassim Jahnke 6e5e115c3b Remove unnecessary rewriting code, fix recipe handlers 2023-08-05 12:28:39 +10:00
Nassim Jahnke fbcbd33c66 Small refactors 2023-08-04 12:35:35 +10:00
Nassim Jahnke 7bea3142bc Fix baseprotocol registration 2023-08-04 12:26:07 +10:00
Nassim Jahnke fb472c3454 Cleanup imports 2023-08-04 12:25:29 +10:00
Nassim Jahnke ac10bb3fd0 Complete 23w31a protocol 2023-08-04 12:22:18 +10:00
Nassim Jahnke 1bc3d407d1 More juicy, untested work 2023-08-03 23:03:42 +10:00
Nassim Jahnke 62c0ef360f More absolutely cursed WIP 2023-08-03 21:58:23 +10:00
Nassim Jahnke 8c6d104faa 23w31a eletric boogaloo 2023-08-03 19:47:18 +10:00
Nassim Jahnke f6b8c4a8c8 Rename rewriter classes 2023-08-03 18:38:18 +10:00
Nassim Jahnke 6586fc1436 More work on the snapshot 2023-08-03 18:36:30 +10:00
Nassim Jahnke 6dfd4747ee Start working on 23w31a 2023-08-03 13:58:22 +10:00
Nassim Jahnke 6377721ebd Fix non item display rotation
Fixes #3383
2023-07-24 20:56:45 +10:00
RK_01 2ec6185c46 Fix <= 1.14 position y sign reading (#3381) 2023-07-12 19:10:49 +02:00
Nassim Jahnke b22907748a Update gradle wrapper 2023-07-11 23:34:37 +02:00
Nassim Jahnke 1aee8a7a6c Don't create ack task when already disabled
Fixes #3373
2023-07-02 13:29:07 +02:00
Nassim Jahnke 7edb43844c Properly handle item display rotation change
Fixes #3354
2023-07-02 13:27:03 +02:00
sandtechnology f28aac5eb3 Handle more null TextComponent for 1.19 to 1.18.2 (#3366) 2023-06-24 11:12:14 +02:00
Nassim Jahnke 4971eeab6a Don't check block items for armor swapping 2023-06-22 11:03:11 +02:00
Pablo Herrera 0bb940e42d Fix missing block connection updates on cancelled place/remove (#3359) 2023-06-20 13:39:02 +02:00
Pablo Herrera e32933ada9 Fix late registration of join listener (#3360) 2023-06-19 17:02:05 +02:00
EnZaXD a817746edc Remove requirement for MovementTransmitterProvider (#3355) 2023-06-18 15:30:58 +02:00
RK_01 408392e796 Fixed some 1.9 -> 1.8 movement issues (#3353) 2023-06-18 14:51:37 +02:00
Kichura 20a4ae3e4b Use friendly names in CI tasks, Update shadow and blossom (#3349) 2023-06-15 09:48:13 +02:00
Nassim Jahnke 08b2d78165 Move dump creation to util class 2023-06-14 23:13:44 +02:00
FlorianMichael 17eb7b676a Remap old smithing menu to furnace in 1.20 2023-06-12 18:57:20 +02:00
Nassim Jahnke 9a1598b9ed Update 1.20/1.20.1 protocol version 2023-06-12 16:47:22 +02:00
Myles 9e7cd151bc Update version table to 1.20 2023-06-11 12:02:25 +01:00
David Luedtke 2a14f9a0cd Fix display rotation change 2023-06-09 12:17:11 +02:00
Ossi Erkkilä bf825d5ef6 Fix SpongeViaInjector#getServerProtocolVersion() for Sponge API-10+ (#3337) 2023-06-09 11:29:55 +02:00
EnZaXD 28ba4afc27 Added VersionInfo#getVersion to bypass Java inlining (#3344) 2023-06-09 08:52:30 +02:00
Nassim Jahnke 7f347231bb Bump version to 4.7.1-SNAPSHOT 2023-06-08 13:21:19 +02:00
115 changed files with 3595 additions and 979 deletions

No files matched your search

+5 -9
View File
@@ -1,6 +1,3 @@
# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: Java CI with Gradle
on: [push, pull_request]
@@ -13,16 +10,15 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- name: Checkout Repository
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 11
check-latest: true
- name: Build with Gradle
run: ./gradlew build
- uses: actions/upload-artifact@v3
with:
name: artifact
path: build/libs
+27
View File
@@ -0,0 +1,27 @@
name: Publish to Hangar and Modrinth
on:
push:
branches:
- master
- dev
jobs:
publish:
if: github.repository_owner == 'ViaVersion'
runs-on: ubuntu-22.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 17
check-latest: true
- name: Publish
env:
HANGAR_TOKEN: ${{ secrets.HANGAR_TOKEN }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
run: ./gradlew build modrinth publishAllPublicationsToHangar --stacktrace
+39 -34
View File
@@ -1,61 +1,49 @@
# ViaVersion - Spigot, Sponge, BungeeCord, Velocity
# ViaVersion
[![Latest Release](https://img.shields.io/github/v/release/ViaVersion/ViaVersion)](https://viaversion.com)
[![Build Status](https://github.com/ViaVersion/ViaVersion/actions/workflows/gradle.yml/badge.svg?branch=master)](https://github.com/ViaVersion/ViaVersion/actions)
[![Discord](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://viaversion.com/discord)
**Allows the connection of higher client versions to lower server versions**
**Allows the connection of higher client versions to lower server versions -
it works on any platform you can think of.**
The base ViaVersion jar runs on Paper, Velocity, Bungee, and Sponge. We also have projects integrating ViaVersion to run
on Fabric, Forge, or as a standalone proxy to join from basically any client version on any server version from the
past decade. **See [HERE](https://github.com/ViaVersion) for an overview of the different Via\* projects.**
Supported Versions:
![Table (https://i.imgur.com/aSjHvQk.png)](https://i.imgur.com/aSjHvQk.png)
On Paper you may also use ProtocolSupport, but ensure you have the right build for your server version.
![Table (https://i.imgur.com/sTrVnC2.png)](https://i.imgur.com/sTrVnC2.png)
**User Docs:** https://docs.viaversion.com/display/VIAVERSION/
Sources
Releases/Dev Builds
--------
**[OpenNBT](https://github.com/ViaVersion/OpenNBT)**
You can find official releases in the following places:
**[Via Mappings Generator](https://github.com/ViaVersion/Mappings)**
- **Hangar (for our plugins)**: https://hangar.papermc.io/ViaVersion/ViaVersion
- **Modrinth (for our mods)**: https://modrinth.com/mod/viaversion
- **GitHub**: https://github.com/ViaVersion/ViaVersion/releases
**[Mojang mappings](https://minecraft.fandom.com/wiki/Obfuscation_map)** (Thank you, Mojang, very cool)
Dev builds for **all** of our projects are on our Jenkins server:
**[wiki.vg](https://wiki.vg)** (Used for historic information regarding packet structure, we also contribute back)
**[Burger](https://github.com/Pokechu22/Burger)** (See [PAaaS](https://github.com/Matsv/Paaas))
Releases / Dev Builds
--------
You can find official releases here:
https://www.spigotmc.org/resources/viaversion.19254/history
- **Jenkins**: https://ci.viaversion.com
ViaVersion as a Dependency
----------
You can find official dev builds here:
**Jenkins:** https://ci.viaversion.com
**JavaDocs:** https://jd.viaversion.com
**Maven Repository:**
**Maven:**
```xml
<repository>
<id>viaversion-repo</id>
<url>https://repo.viaversion.com</url>
</repository>
```
**API-artifact:**
```xml
<dependency>
<groupId>com.viaversion</groupId>
<artifactId>viaversion-api</artifactId>
@@ -64,7 +52,17 @@ You can find official dev builds here:
</dependency>
```
Replace the version depending on your needs.
**Gradle:**
```kotlin
repositories {
maven("https://repo.viaversion.com")
}
dependencies {
compileOnly("com.viaversion:viaversion-api:VERSION") // Replace the version
}
```
If you need access to the existing protocol or platform implementations, use the parent artifact `viaversion`.
Please note the [differences in licensing](#license).
@@ -73,8 +71,7 @@ Please note the [differences in licensing](#license).
Building
--------
After cloning this repository, build the project with Gradle by running `./gradlew build` and take the created jar out
of
the `build/libs` directory.
of the `build/libs` directory.
Mapping Files
@@ -82,13 +79,21 @@ Mapping Files
Mapping files are generated and managed in our [Mappings repository](https://github.com/ViaVersion/Mappings).
Resources
--------
- **[Via Mappings Generator](https://github.com/ViaVersion/Mappings)**
- **[Mojang mappings](https://minecraft.wiki/w/Obfuscation_map)** (Thank you, Mojang, very cool)
- **[wiki.vg](https://wiki.vg)** (Used for historic information regarding packet structure, we also contribute back)
- **[Burger](https://github.com/Pokechu22/Burger)** (See [PAaaS](https://github.com/Matsv/Paaas))
License
--------
The entirety of the [API directory](api) (including the legacy API directory) is licensed under the MIT License;
see [licenses/MIT.md](licenses/MIT.md) for
details.
Everything else, unless explicitly stated otherwise, is licensed under the GNU General Public License, including the end
product as a whole; see [licenses/GPL.md](licenses/GPL.md) for details.
Everything else, unless explicitly stated otherwise, is licensed under the GNU General Public License v3, including the
end-product as a whole; see [licenses/GPL.md](licenses/GPL.md) for details.
Special thanks to all our [Contributors](https://github.com/ViaVersion/ViaVersion/graphs/contributors).
@@ -83,10 +83,11 @@ 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 v1_19_4 = register(762, "1.19.4");
public static final ProtocolVersion v1_20 = register(763, "1.20");
public static final ProtocolVersion v1_20 = register(763, "1.20/1.20.1", new VersionRange("1.20", 0, 1));
public static final ProtocolVersion v1_20_2 = register(764, "1.20.2");
public static final ProtocolVersion unknown = register(-1, "UNKNOWN");
public static ProtocolVersion register(int version, String name) {
+10 -3
View File
@@ -1,11 +1,18 @@
plugins {
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id("via.shadow-conventions")
}
blossom {
replaceToken("\$VERSION", project.version)
replaceToken("\$IMPL_VERSION", "git-ViaVersion-${project.version}:${rootProject.latestCommitHash()}")
sourceSets {
main {
blossom {
javaSources {
property("version", project.version.toString())
property("impl_version", "git-ViaVersion-${project.version}:${rootProject.latestCommitHash()}")
}
}
}
}
dependencies {
@@ -31,5 +31,14 @@ public final class VersionInfo {
*
* @see ViaPlatform#getPluginVersion()
*/
public static final String VERSION = "$VERSION";
public static final String VERSION = "{{ version }}";
private static final String IMPLEMENTATION_VERSION = "{{ impl_version }}";
public static String getVersion() {
return VERSION;
}
public static String getImplementationVersion() {
return IMPLEMENTATION_VERSION;
}
}
@@ -63,7 +63,7 @@ public interface ViaAPI<T> {
* @return API version incremented with meaningful API changes
*/
default int apiVersion() {
return 19;
return 20;
}
/**
@@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.connection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.State;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -33,10 +34,63 @@ public interface ProtocolInfo {
* Returns the protocol state the user is currently in.
*
* @return protocol state
* @deprecated server and client can be in different states, use {@link #getClientState()} or {@link #getServerState()}
*/
State getState();
@Deprecated/*(forRemoval = true)*/
default State getState() {
return this.getServerState();
}
void setState(State state);
/**
* Returns the protocol state the client is currently in.
*
* @return the client protocol state
*/
State getClientState();
/**
* Returns the protocol state the server is currently in.
*
* @return the server protocol state
*/
State getServerState();
/**
* Returns the protocol state for the given direction.
*
* @param direction protocol direction
* @return state for the given direction
*/
default State getState(final Direction direction) {
// Return the state the packet is coming from
return direction == Direction.CLIENTBOUND ? this.getServerState() : this.getClientState();
}
/**
* Sets both client and server state.
*
* @param state the new protocol state
* @see #setClientState(State)
* @see #setServerState(State)
*/
default void setState(final State state) {
this.setClientState(state);
this.setServerState(state);
}
/**
* Sets the client protocol state.
*
* @param clientState the new client protocol state
*/
void setClientState(State clientState);
/**
* Sets the server protocol state.
*
* @param serverState the new server protocol state
*/
void setServerState(State serverState);
/**
* Returns the user's protocol version, or -1 if not set.
@@ -38,4 +38,7 @@ public interface StorableObject {
default boolean clearOnServerSwitch() {
return true;
}
default void onRemove() {
}
}
@@ -50,4 +50,14 @@ public final class Quaternion {
public float w() {
return w;
}
@Override
public String toString() {
return "Quaternion{" +
"x=" + x +
", y=" + y +
", z=" + z +
", w=" + w +
'}';
}
}
@@ -47,6 +47,11 @@ public class ChunkSectionLightImpl implements ChunkSectionLight {
@Override
public void setSkyLight(byte[] data) {
if (data == null) {
this.skyLight = null;
return;
}
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
if (this.skyLight == null) {
this.skyLight = new NibbleArray(data);
@@ -29,21 +29,24 @@ import it.unimi.dsi.fastutil.ints.IntList;
public final class DataPaletteImpl implements DataPalette {
private static final int DEFAULT_INITIAL_SIZE = 16;
private final IntList palette;
private final Int2IntMap inversePalette;
private final int[] values;
private final int sizeBits;
private ChunkData values;
public DataPaletteImpl(final int valuesLength) {
this(valuesLength, 8);
this(valuesLength, DEFAULT_INITIAL_SIZE);
}
public DataPaletteImpl(final int valuesLength, final int expectedPaletteLength) {
this.values = new int[valuesLength];
public DataPaletteImpl(final int valuesLength, final int initialSize) {
values = new EmptyChunkData(valuesLength);
sizeBits = Integer.numberOfTrailingZeros(valuesLength) / 3;
// Pre-size the palette array/map
palette = new IntArrayList(expectedPaletteLength);
inversePalette = new Int2IntOpenHashMap(expectedPaletteLength);
palette = new IntArrayList(initialSize);
// To get an initial table size of initialSize, need to scale it down by load factor.
inversePalette = new Int2IntOpenHashMap((int) (initialSize * Int2IntOpenHashMap.DEFAULT_LOAD_FACTOR));
inversePalette.defaultReturnValue(-1);
}
@@ -54,7 +57,7 @@ public final class DataPaletteImpl implements DataPalette {
@Override
public int idAt(final int sectionCoordinate) {
final int index = values[sectionCoordinate];
final int index = values.get(sectionCoordinate);
return palette.getInt(index);
}
@@ -67,17 +70,17 @@ public final class DataPaletteImpl implements DataPalette {
inversePalette.put(id, index);
}
values[sectionCoordinate] = index;
values.set(sectionCoordinate, index);
}
@Override
public int paletteIndexAt(final int packedCoordinate) {
return values[packedCoordinate];
return values.get(packedCoordinate);
}
@Override
public void setPaletteIndexAt(final int sectionCoordinate, final int index) {
values[sectionCoordinate] = index;
values.set(sectionCoordinate, index);
}
@Override
@@ -131,4 +134,77 @@ public final class DataPaletteImpl implements DataPalette {
palette.clear();
inversePalette.clear();
}
interface ChunkData {
int get(int idx);
void set(int idx, int val);
}
private class EmptyChunkData implements ChunkData {
private final int size;
public EmptyChunkData(int size) {
this.size = size;
}
@Override
public int get(int idx) {
return 0;
}
@Override
public void set(int idx, int val) {
if (val != 0) {
values = new ByteChunkData(size);
values.set(idx, val);
}
}
}
private class ByteChunkData implements ChunkData {
private final byte[] data;
public ByteChunkData(int size) {
this.data = new byte[size];
}
@Override
public int get(int idx) {
return data[idx] & 0xFF;
}
@Override
public void set(int idx, int val) {
// Overflowed size of byte (over 256 different materials), go up to short
if (val > 0xFF) {
values = new ShortChunkData(data);
values.set(idx, val);
return;
}
data[idx] = (byte) val;
}
}
private static class ShortChunkData implements ChunkData {
private final short[] data;
public ShortChunkData(byte[] data) {
this.data = new short[data.length];
for (int i = 0; i < data.length; i++) {
this.data[i] = (short) (data[i] & 0xFF);
}
}
@Override
public int get(int idx) {
return data[idx];
}
@Override
public void set(int idx, int val) {
data[idx] = (short) val;
}
}
}
@@ -0,0 +1,73 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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;
import java.util.Objects;
public final class ChunkPosition {
private final int chunkX;
private final int chunkZ;
public ChunkPosition(int chunkX, int chunkZ) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
}
public ChunkPosition(long chunkKey) {
this.chunkX = (int) chunkKey;
this.chunkZ = (int) (chunkKey >> 32);
}
public int chunkX() {
return chunkX;
}
public int chunkZ() {
return chunkZ;
}
public long chunkKey() {
return (long) chunkX & 0xffffffffL | ((long) chunkZ & 0xffffffffL) << 32;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ChunkPosition that = (ChunkPosition) o;
return chunkX == that.chunkX && chunkZ == that.chunkZ;
}
@Override
public int hashCode() {
return Objects.hash(chunkX, chunkZ);
}
@Override
public String toString() {
return "ChunkPosition{" +
"chunkX=" + chunkX +
", chunkZ=" + chunkZ +
'}';
}
}
@@ -0,0 +1,64 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_20_2 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.ITEM1_20_2);
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 optionalBlockStateType = add(15, Type.VAR_INT);
public final MetaType nbtType = add(16, Type.NAMELESS_NBT);
public final MetaType particleType;
public final MetaType villagerDatatType = add(18, Type.VILLAGER_DATA);
public final MetaType optionalVarIntType = add(19, Type.OPTIONAL_VAR_INT);
public final MetaType poseType = add(20, Type.VAR_INT);
public final MetaType catVariantType = add(21, Type.VAR_INT);
public final MetaType frogVariantType = add(22, Type.VAR_INT);
public final MetaType optionalGlobalPosition = add(23, Type.OPTIONAL_GLOBAL_POSITION);
public final MetaType paintingVariantType = add(24, Type.VAR_INT);
public final MetaType snifferState = add(25, Type.VAR_INT);
public final MetaType vectorType = add(26, Type.VECTOR3F);
public final MetaType quaternionType = add(27, Type.QUATERNION);
public MetaTypes1_20_2(final ParticleType particleType) {
super(28);
this.particleType = add(17, particleType);
}
}
@@ -77,6 +77,7 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
/**
* Creates a protocol with automated id mapping if the respective packet type classes are not null.
* They are also required to track the CONFIGURATION state.
*/
protected AbstractProtocol(@Nullable Class<CU> unmappedClientboundPacketType, @Nullable Class<CM> mappedClientboundPacketType,
@Nullable Class<SM> mappedServerboundPacketType, @Nullable Class<SU> unmappedServerboundPacketType) {
@@ -95,6 +96,7 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
initialized = true;
registerPackets();
registerConfigurationChangeHandlers();
// Register the rest of the ids with no handlers if necessary
if (unmappedClientboundPacketType != null && mappedClientboundPacketType != null
@@ -117,6 +119,33 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
}
}
protected void registerConfigurationChangeHandlers() {
// Register handlers for protocol state switching
// Assuming ids will change too often, it is cleaner to register them here instead of the base protocols,
// even if there will be multiple of these handlers
final SU configurationAcknowledgedPacket = configurationAcknowledgedPacket();
if (configurationAcknowledgedPacket != null) {
registerServerbound(configurationAcknowledgedPacket, setClientStateHandler(State.CONFIGURATION));
}
final CU startConfigurationPacket = startConfigurationPacket();
if (startConfigurationPacket != null) {
registerClientbound(startConfigurationPacket, setServerStateHandler(State.CONFIGURATION));
}
final ServerboundPacketType finishConfigurationPacket = serverboundFinishConfigurationPacket();
if (finishConfigurationPacket != null) {
final int id = finishConfigurationPacket.getId();
registerServerbound(State.CONFIGURATION, id, id, setClientStateHandler(State.PLAY));
}
final ClientboundPacketType clientboundFinishConfigurationPacket = clientboundFinishConfigurationPacket();
if (clientboundFinishConfigurationPacket != null) {
final int id = clientboundFinishConfigurationPacket.getId();
registerClientbound(State.CONFIGURATION, id, id, setServerStateHandler(State.PLAY));
}
}
private <U extends PacketType, M extends PacketType> void registerPacketIdChanges(
Map<State, PacketTypeMap<U>> unmappedPacketTypes,
Map<State, PacketTypeMap<M>> mappedPacketTypes,
@@ -208,6 +237,28 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
return Collections.emptyMap();
}
protected @Nullable SU configurationAcknowledgedPacket() {
final Map<State, PacketTypeMap<SU>> packetTypes = packetTypesProvider.unmappedServerboundPacketTypes();
final PacketTypeMap<SU> packetTypeMap = packetTypes.get(State.PLAY);
return packetTypeMap != null ? packetTypeMap.typeByName("CONFIGURATION_ACKNOWLEDGED") : null;
}
protected @Nullable CU startConfigurationPacket() {
final Map<State, PacketTypeMap<CU>> packetTypes = packetTypesProvider.unmappedClientboundPacketTypes();
final PacketTypeMap<CU> packetTypeMap = packetTypes.get(State.PLAY);
return packetTypeMap != null ? packetTypeMap.typeByName("START_CONFIGURATION") : null;
}
protected @Nullable ServerboundPacketType serverboundFinishConfigurationPacket() {
// To be overridden
return null;
}
protected @Nullable ClientboundPacketType clientboundFinishConfigurationPacket() {
// To be overridden
return null;
}
// ---------------------------------------------------------------------------------
@Override
@@ -361,7 +412,7 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
}
}
private void throwRemapError(Direction direction, State state, int unmappedPacketId, int mappedPacketId, InformativeException e) throws InformativeException {
protected void throwRemapError(Direction direction, State state, int unmappedPacketId, int mappedPacketId, InformativeException e) throws InformativeException {
// Don't print errors during handshake/login/status
if (state != State.PLAY && direction == Direction.SERVERBOUND && !Via.getManager().debugHandler().enabled()) {
e.setShouldBePrinted(false);
@@ -404,6 +455,14 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
}
}
protected PacketHandler setClientStateHandler(final State state) {
return wrapper -> wrapper.user().getProtocolInfo().setClientState(state);
}
protected PacketHandler setServerStateHandler(final State state) {
return wrapper -> wrapper.user().getProtocolInfo().setClientState(state);
}
@Override
public PacketTypesProvider<CU, CM, SM, SU> getPacketTypesProvider() {
return packetTypesProvider;
@@ -69,10 +69,17 @@ public interface ProtocolPipeline extends SimpleProtocol {
/**
* Returns the list of protocols this pipeline contains.
*
* @return list of protocols in this pipe
* @return immutable list of protocols in this pipe
*/
List<Protocol> pipes();
/**
* Returns the list of protocols this pipeline contains in reversed order.
*
* @return immutable list of protocols in reversed direction
*/
List<Protocol> reversedPipes();
/**
* Returns whether this pipe has protocols that are not base protocols, as given by {@link Protocol#isBaseProtocol()}.
*
@@ -27,5 +27,6 @@ public enum State {
HANDSHAKE,
STATUS,
LOGIN,
CONFIGURATION,
PLAY
}
@@ -25,9 +25,11 @@ package com.viaversion.viaversion.api.protocol.packet.provider;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import com.viaversion.viaversion.api.protocol.packet.PacketType;
import org.checkerframework.checker.nullness.qual.Nullable;
final class PacketTypeArrayMap<P> implements PacketTypeMap<P> {
final class PacketTypeArrayMap<P extends PacketType> implements PacketTypeMap<P> {
private final Map<String, P> packetsByName;
private final P[] packets;
@@ -29,7 +29,7 @@ import java.util.HashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface PacketTypeMap<P> {
public interface PacketTypeMap<P extends PacketType> {
/**
* Returns the packet type by the given name.
@@ -67,11 +67,11 @@ public interface PacketTypeMap<P> {
return of(byName, types);
}
static <T> PacketTypeMap<T> of(final Map<String, T> packetsByName, final Int2ObjectMap<T> packetsById) {
static <T extends PacketType> PacketTypeMap<T> of(final Map<String, T> packetsByName, final Int2ObjectMap<T> packetsById) {
return new PacketTypeMapMap<>(packetsByName, packetsById);
}
static <T> PacketTypeMap<T> of(final Map<String, T> packetsByName, final T[] packets) {
static <T extends PacketType> PacketTypeMap<T> of(final Map<String, T> packetsByName, final T[] packets) {
return new PacketTypeArrayMap<>(packetsByName, packets);
}
}
@@ -22,12 +22,13 @@
*/
package com.viaversion.viaversion.api.protocol.packet.provider;
import com.viaversion.viaversion.api.protocol.packet.PacketType;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.Collection;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
final class PacketTypeMapMap<P> implements PacketTypeMap<P> {
final class PacketTypeMapMap<P extends PacketType> implements PacketTypeMap<P> {
private final Map<String, P> packetsByName;
private final Int2ObjectMap<P> packetsById;
@@ -83,7 +83,8 @@ public class ProtocolVersion {
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 v1_19_4 = register(762, "1.19.4");
public static final ProtocolVersion v1_20 = register(763, "1.20");
public static final ProtocolVersion v1_20 = register(763, "1.20/1.20.1", new VersionRange("1.20", 0, 1));
public static final ProtocolVersion v1_20_2 = register(764, "1.20.2");
public static final ProtocolVersion unknown = register(-1, "UNKNOWN");
public static ProtocolVersion register(int version, String name) {
@@ -22,7 +22,6 @@
*/
package com.viaversion.viaversion.api.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
@@ -36,6 +35,7 @@ import com.viaversion.viaversion.api.minecraft.Vector;
import com.viaversion.viaversion.api.minecraft.Vector3f;
import com.viaversion.viaversion.api.minecraft.VillagerData;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.metadata.ChunkPosition;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.BooleanType;
import com.viaversion.viaversion.api.type.types.ByteArrayType;
@@ -59,15 +59,18 @@ import com.viaversion.viaversion.api.type.types.VarIntType;
import com.viaversion.viaversion.api.type.types.VarLongType;
import com.viaversion.viaversion.api.type.types.VoidType;
import com.viaversion.viaversion.api.type.types.minecraft.BlockChangeRecordType;
import com.viaversion.viaversion.api.type.types.minecraft.ChunkPositionType;
import com.viaversion.viaversion.api.type.types.minecraft.EulerAngleType;
import com.viaversion.viaversion.api.type.types.minecraft.FlatItemArrayType;
import com.viaversion.viaversion.api.type.types.minecraft.FlatItemType;
import com.viaversion.viaversion.api.type.types.minecraft.FlatVarIntItemArrayType;
import com.viaversion.viaversion.api.type.types.minecraft.FlatVarIntItemType;
import com.viaversion.viaversion.api.type.types.minecraft.GlobalPositionType;
import com.viaversion.viaversion.api.type.types.minecraft.Item1_20_2Type;
import com.viaversion.viaversion.api.type.types.minecraft.ItemArrayType;
import com.viaversion.viaversion.api.type.types.minecraft.ItemType;
import com.viaversion.viaversion.api.type.types.minecraft.NBTType;
import com.viaversion.viaversion.api.type.types.minecraft.NamelessNBTType;
import com.viaversion.viaversion.api.type.types.minecraft.OptionalVarIntType;
import com.viaversion.viaversion.api.type.types.minecraft.PlayerMessageSignatureType;
import com.viaversion.viaversion.api.type.types.minecraft.Position1_14Type;
@@ -163,10 +166,11 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
public static final Type<Vector3f> VECTOR3F = new Vector3fType();
public static final Type<Quaternion> QUATERNION = new QuaternionType();
public static final Type<CompoundTag> NBT = new NBTType();
public static final Type<CompoundTag> NAMELESS_NBT = new NamelessNBTType();
public static final Type<CompoundTag[]> NBT_ARRAY = new ArrayType<>(Type.NBT);
public static final Type<GlobalPosition> GLOBAL_POSITION = new GlobalPositionType();
public static final Type<GlobalPosition> OPTIONAL_GLOBAL_POSITION = new GlobalPositionType.OptionalGlobalPositionType();
public static final Type<ChunkPosition> CHUNK_POSITION = new ChunkPositionType();
public static final Type<BlockChangeRecord> BLOCK_CHANGE_RECORD = new BlockChangeRecordType();
public static final Type<BlockChangeRecord[]> BLOCK_CHANGE_RECORD_ARRAY = new ArrayType<>(Type.BLOCK_CHANGE_RECORD);
@@ -194,6 +198,9 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
public static final Type<Item[]> FLAT_ITEM_ARRAY_VAR_INT = new ArrayType<>(FLAT_ITEM);
public static final Type<Item[]> FLAT_VAR_INT_ITEM_ARRAY_VAR_INT = new ArrayType<>(FLAT_VAR_INT_ITEM);
public static final Type<Item> ITEM1_20_2 = new Item1_20_2Type();
public static final Type<Item[]> ITEM1_20_2_VAR_INT_ARRAY = new ArrayType<>(ITEM1_20_2);
/* Actual Class */
private final Class<? super T> outputClass;
private final String typeName;
@@ -0,0 +1,45 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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.minecraft;
import com.viaversion.viaversion.api.minecraft.metadata.ChunkPosition;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
public class ChunkPositionType extends Type<ChunkPosition> {
public ChunkPositionType() {
super(ChunkPosition.class);
}
@Override
public ChunkPosition read(ByteBuf buffer) throws Exception {
final long chunkKey = Type.LONG.readPrimitive(buffer);
return new ChunkPosition(chunkKey);
}
@Override
public void write(ByteBuf buffer, ChunkPosition chunkPosition) throws Exception {
Type.LONG.writePrimitive(buffer, chunkPosition.chunkKey());
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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.minecraft;
import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.minecraft.item.Item;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class Item1_20_2Type extends BaseItemType {
public Item1_20_2Type() {
super("Item1_20_2Type");
}
@Override
public @Nullable Item read(final ByteBuf buffer) throws Exception {
if (!buffer.readBoolean()) {
return null;
}
final Item item = new DataItem();
item.setIdentifier(VAR_INT.readPrimitive(buffer));
item.setAmount(buffer.readByte());
item.setTag(NAMELESS_NBT.read(buffer));
return item;
}
@Override
public void write(final ByteBuf buffer, @Nullable final Item object) throws Exception {
if (object == null) {
buffer.writeBoolean(false);
} else {
buffer.writeBoolean(true);
VAR_INT.writePrimitive(buffer, object.identifier());
buffer.writeByte(object.amount());
NAMELESS_NBT.write(buffer, object.tag());
}
}
}
@@ -28,6 +28,7 @@ import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ItemType extends BaseItemType {
public ItemType() {
super("Item");
}
@@ -22,15 +22,14 @@
*/
package com.viaversion.viaversion.api.type.types.minecraft;
import com.github.steveice10.opennbt.NBTIO;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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;
import io.netty.buffer.ByteBufOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.checkerframework.checker.nullness.qual.Nullable;
public class NBTType extends Type<CompoundTag> {
@@ -42,24 +41,45 @@ public class NBTType extends Type<CompoundTag> {
}
@Override
public CompoundTag read(ByteBuf buffer) throws Exception {
int readerIndex = buffer.readerIndex();
byte b = buffer.readByte();
if (b == 0) {
return null;
} else {
buffer.readerIndex(readerIndex);
return NBTIO.readTag((DataInput) new ByteBufInputStream(buffer), TagLimiter.create(MAX_NBT_BYTES, MAX_NESTING_LEVEL));
}
public CompoundTag read(final ByteBuf buffer) throws Exception {
return read(buffer, true);
}
@Override
public void write(ByteBuf buffer, CompoundTag object) throws Exception {
if (object == null) {
buffer.writeByte(0);
} else {
ByteBufOutputStream bytebufStream = new ByteBufOutputStream(buffer);
NBTIO.writeTag((DataOutput) bytebufStream, object);
public void write(final ByteBuf buffer, final CompoundTag object) throws Exception {
write(buffer, object, "");
}
public static CompoundTag read(final ByteBuf buffer, final boolean readName) throws Exception {
final byte id = buffer.readByte();
if (id == 0) {
return null;
}
if (id != CompoundTag.ID) {
throw new IOException(String.format("Expected root tag to be a CompoundTag, was %s", id));
}
if (readName) {
buffer.skipBytes(buffer.readUnsignedShort());
}
final TagLimiter tagLimiter = TagLimiter.create(MAX_NBT_BYTES, MAX_NESTING_LEVEL);
final CompoundTag tag = new CompoundTag();
tag.read(new ByteBufInputStream(buffer), tagLimiter);
return tag;
}
public static void write(final ByteBuf buffer, final CompoundTag tag, final @Nullable String name) throws Exception {
if (tag == null) {
buffer.writeByte(0);
return;
}
final ByteBufOutputStream out = new ByteBufOutputStream(buffer);
out.writeByte(CompoundTag.ID);
if (name != null) {
out.writeUTF(name);
}
tag.write(out);
}
}
@@ -0,0 +1,44 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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.minecraft;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
public class NamelessNBTType extends Type<CompoundTag> {
public NamelessNBTType() {
super(CompoundTag.class);
}
@Override
public CompoundTag read(final ByteBuf buffer) throws Exception {
return NBTType.read(buffer, false);
}
@Override
public void write(final ByteBuf buffer, final CompoundTag tag) throws Exception {
NBTType.write(buffer, tag, null);
}
}
@@ -28,26 +28,24 @@ import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
public class PositionType extends Type<Position> {
public PositionType() {
super(Position.class);
}
@Override
public Position read(ByteBuf buffer) {
long val = buffer.readLong();
long x = (val >> 38); // signed
long y = (val >> 26) & 0xfff; // unsigned
// this shifting madness is used to preserve sign
long z = (val << 38) >> 38; // signed
final long val = buffer.readLong();
final long x = (val >> 38);
final long y = (val << 26) >> 52;
final long z = (val << 38) >> 38;
return new Position((int) x, (short) y, (int) z);
}
@Override
public void write(ByteBuf buffer, Position object) {
buffer.writeLong((((long) object.x() & 0x3ffffff) << 38)
| ((((long) object.y()) & 0xfff) << 26)
| (object.z() & 0x3ffffff));
buffer.writeLong((object.x() & 0X3FFFFFFL) << 38 | (object.y() & 0XFFFL) << 26 | (object.z() & 0X3FFFFFFL));
}
public static final class OptionalPositionType extends OptionalType<Position> {
@@ -0,0 +1,53 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntityImpl;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
public class BlockEntityType1_20_2 extends Type<BlockEntity> {
public BlockEntityType1_20_2() {
super(BlockEntity.class);
}
@Override
public BlockEntity read(final ByteBuf buffer) throws Exception {
final byte xz = buffer.readByte();
final short y = buffer.readShort();
final int typeId = Type.VAR_INT.readPrimitive(buffer);
final CompoundTag tag = Type.NAMELESS_NBT.read(buffer);
return new BlockEntityImpl(xz, y, typeId, tag);
}
@Override
public void write(final ByteBuf buffer, final BlockEntity entity) throws Exception {
buffer.writeByte(entity.packedXZ());
buffer.writeShort(entity.y());
Type.VAR_INT.writePrimitive(buffer, entity.typeId());
Type.NAMELESS_NBT.write(buffer, entity.tag());
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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.blockentity.BlockEntity;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import com.viaversion.viaversion.api.minecraft.metadata.types.MetaTypes1_20_2;
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_20_2 {
public static final Type<BlockEntity> BLOCK_ENTITY = new BlockEntityType1_20_2();
public static final ParticleType PARTICLE = Types1_20.PARTICLE; // Only safe to use after protocol loading
public static final MetaTypes1_20_2 META_TYPES = new MetaTypes1_20_2(PARTICLE);
public static final Type<Metadata> METADATA = new MetadataType(META_TYPES);
public static final Type<List<Metadata>> METADATA_LIST = new MetaListType(METADATA);
}
+1 -1
View File
@@ -8,5 +8,5 @@ repositories {
dependencies {
// version must be manually kept in sync with the one in root project settings.gradle.kts
implementation("com.github.johnrengelman", "shadow", "8.1.0")
implementation("com.github.johnrengelman", "shadow", "8.1.1")
}
+18
View File
@@ -38,6 +38,24 @@ fun Project.latestCommitHash(): String {
return byteOut.toString(Charsets.UTF_8.name()).trim()
}
fun Project.latestCommitMessage(): String {
val byteOut = ByteArrayOutputStream()
exec {
commandLine = listOf("git", "log", "-1", "--pretty=%B")
standardOutput = byteOut
}
return byteOut.toString(Charsets.UTF_8.name()).trim()
}
fun Project.branchName(): String {
val byteOut = ByteArrayOutputStream()
exec {
commandLine = listOf("git", "rev-parse", "--abbrev-ref", "HEAD")
standardOutput = byteOut
}
return byteOut.toString(Charsets.UTF_8.name()).trim()
}
fun JavaPluginExtension.javaTarget(version: Int) {
toolchain.languageVersion.set(JavaLanguageVersion.of(version))
}
@@ -103,6 +103,12 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
manager.init();
}
if (Via.getConfig().shouldRegisterUserConnectionOnJoin()) {
// When event priority ties, registration order is used.
// Must register without delay to ensure other plugins on lowest get the fix applied.
getServer().getPluginManager().registerEvents(new JoinListener(), this);
}
if (FOLIA) {
// Use Folia's RegionizedServerInitEvent to run code after the server has loaded
final Class<? extends Event> serverInitEventClass;
@@ -46,8 +46,8 @@ public class JoinListener implements Listener {
Field gamePacketListenerField = null, connectionField = null, channelField = null;
try {
getHandleMethod = NMSUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle");
gamePacketListenerField = findField(getHandleMethod.getReturnType(), "PlayerConnection", "ServerGamePacketListenerImpl");
connectionField = findField(gamePacketListenerField.getType(), "NetworkManager", "Connection");
gamePacketListenerField = findField(false, getHandleMethod.getReturnType(), "PlayerConnection", "ServerGamePacketListenerImpl");
connectionField = findField(true, gamePacketListenerField.getType(), "NetworkManager", "Connection");
channelField = findField(connectionField.getType(), Class.forName("io.netty.channel.Channel"));
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) {
Via.getPlatform().getLogger().log(
@@ -63,8 +63,8 @@ public class JoinListener implements Listener {
}
// Loosely search a field with any name, as long as it matches a type name.
private static Field findField(Class<?> clazz, String... types) throws NoSuchFieldException {
for (Field field : clazz.getDeclaredFields()) {
private static Field findField(boolean all, Class<?> clazz, String... types) throws NoSuchFieldException {
for (Field field : all ? clazz.getFields() : clazz.getDeclaredFields()) {
String fieldTypeName = field.getType().getSimpleName();
for (String type : types) {
if (!fieldTypeName.equals(type)) {
@@ -39,12 +39,16 @@ public final class ArmorToggleListener extends ViaBukkitListener {
public void itemUse(final PlayerInteractEvent event) {
final Player player = event.getPlayer();
final ItemStack item = event.getItem();
if (item == null || event.getHand() == null || !isOnPipe(player)) {
if (item == null || event.getHand() == null) {
return;
}
final EquipmentSlot armorItemSlot = item.getType().getEquipmentSlot();
if (armorItemSlot != EquipmentSlot.HAND && armorItemSlot != EquipmentSlot.OFF_HAND) {
if (armorItemSlot == EquipmentSlot.HAND || armorItemSlot == EquipmentSlot.OFF_HAND || item.getType().isBlock()) {
return;
}
if (isOnPipe(player)) {
final PlayerInventory inventory = player.getInventory();
final ItemStack armor = inventory.getItem(armorItemSlot);
// If two pieces of armor are equal, the client will do nothing.
@@ -79,10 +79,6 @@ public class BukkitViaLoader implements ViaPlatformLoader {
public void load() {
registerListener(new UpdateListener());
if (Via.getConfig().shouldRegisterUserConnectionOnJoin()) {
registerListener(new JoinListener());
}
/* Base Protocol */
final ViaVersionPlugin plugin = (ViaVersionPlugin) Bukkit.getPluginManager().getPlugin("ViaVersion");
@@ -39,7 +39,10 @@ public final class BukkitAckSequenceProvider extends AckSequenceProvider {
if (previousSequence == -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);
if (plugin.isEnabled()) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new AckSequenceTask(connection, sequenceStorage), delay);
}
}
}
}
@@ -25,6 +25,9 @@ import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.MovementTrac
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.channel.ChannelHandlerContext;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -79,17 +82,15 @@ public class BukkitViaMovementTransmitter extends MovementTransmitterProvider {
}
}
@Override
public Object getFlyingPacket() {
if (idlePacket == null)
throw new NullPointerException("Could not locate flying packet");
if (idlePacket == null) throw new NullPointerException("Could not locate flying packet");
return idlePacket;
}
@Override
public Object getGroundPacket() {
if (idlePacket == null)
throw new NullPointerException("Could not locate flying packet");
if (idlePacket == null) throw new NullPointerException("Could not locate flying packet");
return idlePacket2;
}
@@ -112,7 +113,15 @@ public class BukkitViaMovementTransmitter extends MovementTransmitterProvider {
}
}
} else {
super.sendPlayer(info);
ChannelHandlerContext context = PipelineUtil.getContextBefore("decoder", info.getChannel().pipeline());
if (context != null) {
if (info.get(MovementTracker.class).isGround()) {
context.fireChannelRead(getGroundPacket());
} else {
context.fireChannelRead(getFlyingPacket());
}
info.get(MovementTracker.class).incrementIdlePacket();
}
}
}
}
@@ -28,12 +28,10 @@ import com.viaversion.viaversion.bungee.listeners.UpdateListener;
import com.viaversion.viaversion.bungee.providers.BungeeBossBarProvider;
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.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;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -71,7 +69,6 @@ public class BungeeViaLoader implements ViaPlatformLoader {
Via.getManager().getProviders().use(EntityIdProvider.class, new BungeeEntityIdProvider());
if (Via.getAPI().getServerVersion().lowestSupportedVersion() < ProtocolVersion.v1_9.getVersion()) {
Via.getManager().getProviders().use(MovementTransmitterProvider.class, new BungeeMovementTransmitter());
Via.getManager().getProviders().use(BossBarProvider.class, new BungeeBossBarProvider());
Via.getManager().getProviders().use(MainHandProvider.class, new BungeeMainHandProvider());
}
@@ -1,53 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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.bungee.providers;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_8.ServerboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.MovementTracker;
public class BungeeMovementTransmitter extends MovementTransmitterProvider {
@Override
public Object getFlyingPacket() {
return null;
}
@Override
public Object getGroundPacket() {
return null;
}
public void sendPlayer(UserConnection userConnection) {
if (userConnection.getProtocolInfo().getState() == State.PLAY) {
PacketWrapper wrapper = PacketWrapper.create(ServerboundPackets1_8.PLAYER_MOVEMENT, null, userConnection);
MovementTracker tracker = userConnection.get(MovementTracker.class);
wrapper.write(Type.BOOLEAN, tracker.isGround());
try {
wrapper.scheduleSendToServer(Protocol1_9To1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
tracker.incrementIdlePacket();
}
}
}
-9
View File
@@ -1,12 +1,3 @@
plugins {
id("net.kyori.blossom")
}
blossom {
replaceToken("\$VERSION", project.version)
replaceToken("\$IMPL_VERSION", "git-ViaVersion-${project.version}:${rootProject.latestCommitHash()}")
}
dependencies {
api(projects.viaversionApi)
api(projects.viaversionApiLegacy)
@@ -17,32 +17,9 @@
*/
package com.viaversion.viaversion.commands.defaultsubs;
import com.google.common.io.CharStreams;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.command.ViaSubCommand;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.dump.DumpTemplate;
import com.viaversion.viaversion.dump.VersionInfo;
import com.viaversion.viaversion.util.GsonUtil;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InvalidObjectException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Level;
import com.viaversion.viaversion.util.DumpUtil;
public class DumpSubCmd extends ViaSubCommand {
@@ -57,124 +34,15 @@ public class DumpSubCmd extends ViaSubCommand {
}
@Override
public boolean execute(ViaCommandSender sender, String[] args) {
VersionInfo version = new VersionInfo(
System.getProperty("java.version"),
System.getProperty("os.name"),
Via.getAPI().getServerVersion().lowestSupportedVersion(),
Via.getManager().getProtocolManager().getSupportedVersions(),
Via.getPlatform().getPlatformName(),
Via.getPlatform().getPlatformVersion(),
Via.getPlatform().getPluginVersion(),
"$IMPL_VERSION",
Via.getManager().getSubPlatforms()
);
Map<String, Object> configuration = Via.getPlatform().getConfigurationProvider().getValues();
DumpTemplate template = new DumpTemplate(version, configuration, Via.getPlatform().getDump(), Via.getManager().getInjector().getDump(), getPlayerSample(sender.getUUID()));
Via.getPlatform().runAsync(new Runnable() {
@Override
public void run() {
HttpURLConnection con;
try {
con = (HttpURLConnection) new URL("https://dump.viaversion.com/documents").openConnection();
} catch (IOException e) {
sender.sendMessage("§4Failed to dump, please check the console for more information");
Via.getPlatform().getLogger().log(Level.WARNING, "Could not paste ViaVersion dump to ViaVersion Dump", e);
return;
}
try {
con.setRequestProperty("Content-Type", "application/json");
con.addRequestProperty("User-Agent", "ViaVersion/" + version.getPluginVersion());
con.setRequestMethod("POST");
con.setDoOutput(true);
OutputStream out = con.getOutputStream();
out.write(new GsonBuilder().setPrettyPrinting().create().toJson(template).getBytes(StandardCharsets.UTF_8));
out.close();
if (con.getResponseCode() == 429) {
sender.sendMessage("§4You can only paste once every minute to protect our systems.");
return;
}
String rawOutput = CharStreams.toString(new InputStreamReader(con.getInputStream()));
con.getInputStream().close();
JsonObject output = GsonUtil.getGson().fromJson(rawOutput, JsonObject.class);
if (!output.has("key"))
throw new InvalidObjectException("Key is not given in Hastebin output");
sender.sendMessage("§2We've made a dump with useful information, report your issue and provide this url: " + getUrl(output.get("key").getAsString()));
} catch (Exception e) {
sender.sendMessage("§4Failed to dump, please check the console for more information");
Via.getPlatform().getLogger().log(Level.WARNING, "Could not paste ViaVersion dump to Hastebin", e);
try {
if (con.getResponseCode() < 200 || con.getResponseCode() > 400) {
String rawOutput = CharStreams.toString(new InputStreamReader(con.getErrorStream()));
con.getErrorStream().close();
Via.getPlatform().getLogger().log(Level.WARNING, "Page returned: " + rawOutput);
}
} catch (IOException e1) {
Via.getPlatform().getLogger().log(Level.WARNING, "Failed to capture further info", e1);
}
}
public boolean execute(final ViaCommandSender sender, final String[] args) {
DumpUtil.postDump(sender.getUUID()).whenComplete((url, e) -> {
if (e != null) {
sender.sendMessage("§4" + e.getMessage());
return;
}
});
sender.sendMessage("§2We've made a dump with useful information, report your issue and provide this url: " + url);
});
return true;
}
private String getUrl(String id) {
return String.format("https://dump.viaversion.com/%s", id);
}
private JsonObject getPlayerSample(UUID senderUuid) {
JsonObject playerSample = new JsonObject();
// Player versions
JsonObject versions = new JsonObject();
playerSample.add("versions", versions);
Map<ProtocolVersion, Integer> playerVersions = new TreeMap<>((o1, o2) -> ProtocolVersion.getIndex(o2) - ProtocolVersion.getIndex(o1));
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
ProtocolVersion protocolVersion = ProtocolVersion.getProtocol(connection.getProtocolInfo().getProtocolVersion());
playerVersions.compute(protocolVersion, (v, num) -> num != null ? num + 1 : 1);
}
for (Map.Entry<ProtocolVersion, Integer> entry : playerVersions.entrySet()) {
versions.addProperty(entry.getKey().getName(), entry.getValue());
}
// Pipeline of sender
Set<List<String>> pipelines = new HashSet<>();
UserConnection senderConnection = Via.getAPI().getConnection(senderUuid);
if (senderConnection != null && senderConnection.getChannel() != null) {
pipelines.add(senderConnection.getChannel().pipeline().names());
}
// Other pipelines if different ones are found (3 max)
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
if (connection.getChannel() == null) {
continue;
}
List<String> names = connection.getChannel().pipeline().names();
if (pipelines.add(names) && pipelines.size() == 3) {
break;
}
}
int i = 0;
for (List<String> pipeline : pipelines) {
JsonArray senderPipeline = new JsonArray(pipeline.size());
for (String name : pipeline) {
senderPipeline.add(name);
}
playerSample.add("pipeline-" + i++, senderPipeline);
}
return playerSample;
}
}
@@ -72,6 +72,8 @@ public class ConnectionManagerImpl implements ConnectionManager {
UUID id = connection.getProtocolInfo().getUuid();
clients.remove(id);
}
connection.clearStoredObjects();
}
@Override
@@ -17,6 +17,7 @@
*/
package com.viaversion.viaversion.connection;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
@@ -26,25 +27,42 @@ import java.util.UUID;
public class ProtocolInfoImpl implements ProtocolInfo {
private final UserConnection connection;
private State state = State.HANDSHAKE;
private State clientState = State.HANDSHAKE;
private State serverState = State.HANDSHAKE;
private int protocolVersion = -1;
private int serverProtocolVersion = -1;
private String username;
private UUID uuid;
private ProtocolPipeline pipeline;
public ProtocolInfoImpl(UserConnection connection) {
public ProtocolInfoImpl(final UserConnection connection) {
this.connection = connection;
}
@Override
public State getState() {
return state;
public State getClientState() {
return clientState;
}
@Override
public void setState(State state) {
this.state = state;
public void setClientState(final State clientState) {
if (Via.getManager().debugHandler().enabled()) {
Via.getPlatform().getLogger().info("Client state changed from " + this.clientState + " to " + clientState + " for " + connection.getProtocolInfo().getUuid());
}
this.clientState = clientState;
}
@Override
public State getServerState() {
return serverState;
}
@Override
public void setServerState(final State serverState) {
if (Via.getManager().debugHandler().enabled()) {
Via.getPlatform().getLogger().info("Server state changed from " + this.serverState + " to " + serverState + " for " + connection.getProtocolInfo().getUuid());
}
this.serverState = serverState;
}
@Override
@@ -108,7 +126,8 @@ public class ProtocolInfoImpl implements ProtocolInfo {
@Override
public String toString() {
return "ProtocolInfo{" +
"state=" + state +
"clientState=" + clientState +
", serverState=" + serverState +
", protocolVersion=" + protocolVersion +
", serverProtocolVersion=" + serverProtocolVersion +
", username='" + username + '\'' +
@@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketTracker;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl;
@@ -94,12 +95,19 @@ public class UserConnectionImpl implements UserConnection {
@Override
public <T extends StorableObject> @Nullable T remove(Class<T> objectClass) {
return (T) storedObjects.remove(objectClass);
final StorableObject object = storedObjects.remove(objectClass);
if (object != null) {
object.onRemove();
}
return (T) object;
}
@Override
public void put(StorableObject object) {
storedObjects.put(object.getClass(), object);
final StorableObject previousObject = storedObjects.put(object.getClass(), object);
if (previousObject != null) {
previousObject.onRemove();
}
}
@Override
@@ -122,12 +130,21 @@ public class UserConnectionImpl implements UserConnection {
@Override
public void clearStoredObjects(boolean isServerSwitch) {
if (isServerSwitch) {
storedObjects.values().removeIf(StorableObject::clearOnServerSwitch);
storedObjects.values().removeIf(storableObject -> {
if (storableObject.clearOnServerSwitch()) {
storableObject.onRemove();
return true;
}
return false;
});
for (EntityTracker tracker : entityTrackers.values()) {
tracker.clearEntities();
tracker.trackClientEntity();
}
} else {
for (StorableObject object : storedObjects.values()) {
object.onRemove();
}
storedObjects.clear();
entityTrackers.clear();
}
@@ -308,8 +325,9 @@ public class UserConnectionImpl implements UserConnection {
}
PacketWrapper wrapper = new PacketWrapperImpl(id, buf, this);
State state = protocolInfo.getState(direction);
try {
protocolInfo.getPipeline().transform(direction, protocolInfo.getState(), wrapper);
protocolInfo.getPipeline().transform(direction, state, wrapper);
} catch (CancelException ex) {
throw cancelSupplier.apply(ex);
}
@@ -37,7 +37,7 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
private final UserConnection connection;
private final EntityType playerType;
private int clientEntityId = -1;
private int currentWorldSectionHeight = 16;
private int currentWorldSectionHeight = -1;
private int currentMinY;
private String currentWorld;
private int biomesSent = -1;
@@ -70,18 +70,20 @@ import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.Protocol1_19_1To
import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.Protocol1_19_3To1_19_1;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.Protocol1_19_4To1_19_3;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.Protocol1_19To1_18_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.Protocol1_20_2To1_20;
import com.viaversion.viaversion.protocols.protocol1_20to1_19_4.Protocol1_20To1_19_4;
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;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_9_1.Protocol1_9To1_9_1;
import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
import org.checkerframework.checker.nullness.qual.Nullable;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -102,8 +104,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.Nullable;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
public class ProtocolManagerImpl implements ProtocolManager {
private static final Protocol BASE_PROTOCOL = new BaseProtocol();
@@ -140,8 +140,6 @@ public class ProtocolManagerImpl implements ProtocolManager {
registerProtocol(new Protocol1_9_1To1_9(), Arrays.asList(ProtocolVersion.v1_9_1.getVersion(), ProtocolVersion.v1_9_2.getVersion()), ProtocolVersion.v1_9.getVersion());
registerProtocol(new Protocol1_9_3To1_9_1_2(), ProtocolVersion.v1_9_3, ProtocolVersion.v1_9_2);
registerProtocol(new Protocol1_9To1_9_1(), ProtocolVersion.v1_9, ProtocolVersion.v1_9_1);
registerProtocol(new Protocol1_9_1_2To1_9_3_4(), Arrays.asList(ProtocolVersion.v1_9_1.getVersion(), ProtocolVersion.v1_9_2.getVersion()), ProtocolVersion.v1_9_3.getVersion());
registerProtocol(new Protocol1_10To1_9_3_4(), ProtocolVersion.v1_10, ProtocolVersion.v1_9_3);
registerProtocol(new Protocol1_11To1_10(), ProtocolVersion.v1_11, ProtocolVersion.v1_10);
@@ -183,6 +181,7 @@ public class ProtocolManagerImpl implements ProtocolManager {
registerProtocol(new Protocol1_19_4To1_19_3(), ProtocolVersion.v1_19_4, ProtocolVersion.v1_19_3);
registerProtocol(new Protocol1_20To1_19_4(), ProtocolVersion.v1_20, ProtocolVersion.v1_19_4);
registerProtocol(new Protocol1_20_2To1_20(), ProtocolVersion.v1_20_2, ProtocolVersion.v1_20);
}
@Override
@@ -17,7 +17,6 @@
*/
package com.viaversion.viaversion.protocol;
import com.google.common.collect.Sets;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.debug.DebugHandler;
@@ -30,9 +29,10 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -42,8 +42,10 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
/**
* Protocol list ordered from client to server transforation with the base protocols at the end.
*/
private List<Protocol> protocolList;
private Set<Class<? extends Protocol>> protocolSet;
private final List<Protocol> protocolList = new CopyOnWriteArrayList<>();
private final Set<Class<? extends Protocol>> protocolSet = new HashSet<>();
private List<Protocol> reversedProtocolList = new CopyOnWriteArrayList<>();
private int baseProtocols;
public ProtocolPipelineImpl(UserConnection userConnection) {
this.userConnection = userConnection;
@@ -53,14 +55,12 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
@Override
protected void registerPackets() {
protocolList = new CopyOnWriteArrayList<>();
// Create a backing set for faster contains calls with larger pipes
protocolSet = Sets.newSetFromMap(new ConcurrentHashMap<>());
// This is a pipeline so we register basic pipes
Protocol baseProtocol = Via.getManager().getProtocolManager().getBaseProtocol();
final Protocol<?, ?, ?, ?> baseProtocol = Via.getManager().getProtocolManager().getBaseProtocol();
protocolList.add(baseProtocol);
reversedProtocolList.add(baseProtocol);
protocolSet.add(baseProtocol.getClass());
baseProtocols++;
}
@Override
@@ -69,44 +69,38 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
@Override
public void add(Protocol protocol) {
protocolList.add(protocol);
public synchronized void add(final Protocol protocol) {
if (protocol.isBaseProtocol()) {
// Add base protocol on top of previous ones
protocolList.add(baseProtocols, protocol);
reversedProtocolList.add(baseProtocols, protocol);
baseProtocols++;
} else {
protocolList.add(protocol);
reversedProtocolList.add(0, protocol);
}
protocolSet.add(protocol.getClass());
protocol.init(userConnection);
if (!protocol.isBaseProtocol()) {
moveBaseProtocolsToTail();
}
}
@Override
public void add(Collection<Protocol> protocols) {
public synchronized void add(final Collection<Protocol> protocols) {
protocolList.addAll(protocols);
for (Protocol protocol : protocols) {
for (final Protocol protocol : protocols) {
protocol.init(userConnection);
this.protocolSet.add(protocol.getClass());
protocolSet.add(protocol.getClass());
}
moveBaseProtocolsToTail();
refreshReversedList();
}
private void moveBaseProtocolsToTail() {
// Move base Protocols to the end, so the login packets can be modified by other protocols
List<Protocol> baseProtocols = null;
for (Protocol protocol : protocolList) {
if (protocol.isBaseProtocol()) {
if (baseProtocols == null) {
baseProtocols = new ArrayList<>();
}
baseProtocols.add(protocol);
}
}
if (baseProtocols != null) {
protocolList.removeAll(baseProtocols);
protocolList.addAll(baseProtocols);
}
private synchronized void refreshReversedList() {
final List<Protocol> protocols = new ArrayList<>(protocolList.subList(0, this.baseProtocols));
final List<Protocol> additionalProtocols = new ArrayList<>(protocolList.subList(this.baseProtocols, protocolList.size()));
Collections.reverse(additionalProtocols);
protocols.addAll(additionalProtocols);
reversedProtocolList = new CopyOnWriteArrayList<>(protocols);
}
@Override
@@ -119,7 +113,7 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
// Apply protocols
packetWrapper.apply(direction, state, 0, protocolList, direction == Direction.CLIENTBOUND);
packetWrapper.apply(direction, state, 0, protocolListFor(direction));
super.transform(direction, state, packetWrapper);
if (debugHandler.enabled() && debugHandler.logPostPacketTransform() && debugHandler.shouldLog(packetWrapper, direction)) {
@@ -127,6 +121,10 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
}
private List<Protocol> protocolListFor(final Direction direction) {
return Collections.unmodifiableList(direction == Direction.SERVERBOUND ? protocolList : reversedProtocolList);
}
private void logPacket(Direction direction, State state, PacketWrapper packetWrapper, int originalID) {
// Debug packet
int clientProtocol = userConnection.getProtocolInfo().getProtocolVersion();
@@ -166,7 +164,12 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
@Override
public List<Protocol> pipes() {
return protocolList;
return Collections.unmodifiableList(protocolList);
}
@Override
public List<Protocol> reversedPipes() {
return Collections.unmodifiableList(reversedProtocolList);
}
@Override
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.protocol.packet;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
@@ -308,12 +309,18 @@ public class PacketWrapperImpl implements PacketWrapper {
* @throws Exception if it fails to write
*/
private ByteBuf constructPacket(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, Direction direction) throws Exception {
// Apply current pipeline - for outgoing protocol, the collection will be reversed in the apply method
Protocol[] protocols = user().getProtocolInfo().getPipeline().pipes().toArray(PROTOCOL_ARRAY);
boolean reverse = direction == Direction.CLIENTBOUND;
final ProtocolInfo protocolInfo = user().getProtocolInfo();
final List<Protocol> pipes = direction == Direction.SERVERBOUND ? protocolInfo.getPipeline().pipes() : protocolInfo.getPipeline().reversedPipes();
final List<Protocol> protocols = new ArrayList<>();
int index = -1;
for (int i = 0; i < protocols.length; i++) {
if (protocols[i].getClass() == packetProtocol) {
for (int i = 0; i < pipes.size(); i++) {
// Always add base protocols to the head
final Protocol protocol = pipes.get(i);
if (protocol.isBaseProtocol()) {
protocols.add(protocol);
}
if (protocol.getClass() == packetProtocol) {
index = i;
break;
}
@@ -325,15 +332,18 @@ public class PacketWrapperImpl implements PacketWrapper {
}
if (skipCurrentPipeline) {
index = reverse ? index - 1 : index + 1;
index = Math.min(index + 1, pipes.size());
}
// Add remaining protocols on top
protocols.addAll(pipes.subList(index, pipes.size()));
// Reset reader before we start
resetReader();
// Apply other protocols
apply(direction, user().getProtocolInfo().getState(), index, protocols, reverse);
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
apply(direction, protocolInfo.getState(direction), 0, protocols);
final ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
return output.retain();
@@ -402,15 +412,22 @@ public class PacketWrapperImpl implements PacketWrapper {
private PacketWrapperImpl apply(Direction direction, State state, int index, Protocol[] pipeline, boolean reverse) throws Exception {
// Reset the reader after every transformation for the packetWrapper, so it can be recycled across packets
State updatedState = state; // The state might change while transforming, so we need to check for that
if (reverse) {
for (int i = index; i >= 0; i--) {
pipeline[i].transform(direction, state, this);
pipeline[i].transform(direction, updatedState, this);
resetReader();
if (this.packetType != null) {
updatedState = this.packetType.state();
}
}
} else {
for (int i = index; i < pipeline.length; i++) {
pipeline[i].transform(direction, state, this);
pipeline[i].transform(direction, updatedState, this);
resetReader();
if (this.packetType != null) {
updatedState = this.packetType.state();
}
}
}
return this;
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.protocols.base;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
import java.util.UUID;
public class BaseProtocol1_16 extends BaseProtocol1_7 {
@@ -45,9 +45,6 @@ public class BaseProtocol1_7 extends AbstractProtocol {
@Override
protected void registerPackets() {
/* Outgoing Packets */
// Status Response Packet
registerClientbound(ClientboundStatusPackets.STATUS_RESPONSE, new PacketHandlers() { // Status Response Packet
@Override
public void register() {
@@ -122,7 +119,9 @@ public class BaseProtocol1_7 extends AbstractProtocol {
// Login Success Packet
registerClientbound(ClientboundLoginPackets.GAME_PROFILE, wrapper -> {
ProtocolInfo info = wrapper.user().getProtocolInfo();
info.setState(State.PLAY);
if (info.getProtocolVersion() < ProtocolVersion.v1_20_2.getVersion()) { // On 1.20.2+, wait for the login ack
info.setState(State.PLAY);
}
UUID uuid = passthroughLoginUUID(wrapper);
info.setUuid(uuid);
@@ -147,7 +146,6 @@ public class BaseProtocol1_7 extends AbstractProtocol {
}
});
/* Incoming Packets */
// Login Start Packet
registerServerbound(ServerboundLoginPackets.HELLO, wrapper -> {
int protocol = wrapper.user().getProtocolInfo().getProtocolVersion();
@@ -164,6 +162,11 @@ public class BaseProtocol1_7 extends AbstractProtocol {
future.addListener(f -> wrapper.user().getChannel().close());
}
});
registerServerbound(ServerboundLoginPackets.LOGIN_ACKNOWLEDGED, wrapper -> {
final ProtocolInfo info = wrapper.user().getProtocolInfo();
info.setState(State.CONFIGURATION);
});
}
@Override
@@ -23,7 +23,8 @@ import com.viaversion.viaversion.api.protocol.packet.State;
public enum ServerboundLoginPackets implements ServerboundPacketType {
HELLO, // 0x00
ENCRYPTION_KEY, // 0x01
CUSTOM_QUERY; // 0x02
CUSTOM_QUERY_ANSWER, // 0x02
LOGIN_ACKNOWLEDGED; // 0x03
@Override
public final int getId() {
@@ -31,9 +31,9 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.protocols.protocol1_10to1_9_3.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_10to1_9_3.storage.ResourcePackTracker;
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;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_3_4Type;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -33,9 +33,9 @@ import com.viaversion.viaversion.protocols.protocol1_11to1_10.data.PotionColorMa
import com.viaversion.viaversion.protocols.protocol1_11to1_10.metadata.MetadataRewriter1_11To1_10;
import com.viaversion.viaversion.protocols.protocol1_11to1_10.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_11to1_10.storage.EntityTracker1_11;
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;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_3_4Type;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.rewriter.SoundRewriter;
import com.viaversion.viaversion.util.Pair;
@@ -40,9 +40,9 @@ import com.viaversion.viaversion.protocols.protocol1_12to1_11_1.packets.Inventor
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;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_3_4Type;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.rewriter.SoundRewriter;
@@ -64,6 +64,8 @@ public final class ConnectionData {
}
public static void update(UserConnection user, Position position) throws Exception {
Boolean inSync = null;
for (BlockFace face : BlockFace.values()) {
Position pos = position.getRelative(face);
int blockState = blockConnectionProvider.getBlockData(user, pos.x(), pos.y(), pos.z());
@@ -73,8 +75,14 @@ public final class ConnectionData {
}
int newBlockState = handler.connect(user, pos, blockState);
if (newBlockState == blockState && blockConnectionProvider.storesBlocks()) {
continue;
if (newBlockState == blockState) {
if (inSync == null) {
inSync = blockConnectionProvider.storesBlocks(user, position);
}
// Blocks-states are the same, and known to be stored and not de-synced, skip update
if (inSync) {
continue;
}
}
updateBlockStorage(user, pos.x(), pos.y(), pos.z(), newBlockState);
@@ -100,8 +108,13 @@ public final class ConnectionData {
blockConnectionProvider.clearStorage(connection);
}
public static void markModified(UserConnection connection, Position pos) {
if (!needStoreBlocks()) return;
blockConnectionProvider.modifiedBlock(connection, pos);
}
public static boolean needStoreBlocks() {
return blockConnectionProvider.storesBlocks();
return blockConnectionProvider.storesBlocks(null, null);
}
public static void connectBlocks(UserConnection user, Chunk chunk) {
@@ -726,7 +739,7 @@ public final class ConnectionData {
Position pos = new Position(x, y, z);
int newBlockState = handler.connect(user, pos, blockState);
if (blockState != newBlockState || !blockConnectionProvider.storesBlocks()) {
if (blockState != newBlockState || !blockConnectionProvider.storesBlocks(user, null)) {
records.add(new BlockChangeRecord1_8(x & 0xF, y, z & 0xF, newBlockState));
updateBlockStorage(user, x, y, z, newBlockState);
}
@@ -18,8 +18,10 @@
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.platform.providers.Provider;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BlockConnectionProvider implements Provider {
@@ -44,6 +46,10 @@ public class BlockConnectionProvider implements Provider {
}
public void modifiedBlock(UserConnection connection, Position position) {
}
public void unloadChunk(UserConnection connection, int x, int z) {
}
@@ -52,7 +58,14 @@ public class BlockConnectionProvider implements Provider {
}
public boolean storesBlocks() {
/**
* True if blocks are stored, and are known to be accurate around the given position.
* If the client has modified the position (ie: placed or broken a block) this should return false.
*
* @param position The position at which block reliability should be checked, null for general-purpose
* @return true if the block & its neighbors are known to be correct
*/
public boolean storesBlocks(UserConnection user, @Nullable Position position) {
return false;
}
@@ -18,7 +18,9 @@
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockConnectionStorage;
import org.checkerframework.checker.nullness.qual.Nullable;
public class PacketBlockConnectionProvider extends BlockConnectionProvider {
@@ -42,6 +44,10 @@ public class PacketBlockConnectionProvider extends BlockConnectionProvider {
connection.get(BlockConnectionStorage.class).clear();
}
public void modifiedBlock(UserConnection connection, Position position) {
connection.get(BlockConnectionStorage.class).markModified(position);
}
@Override
public void unloadChunk(UserConnection connection, int x, int z) {
connection.get(BlockConnectionStorage.class).unloadChunk(x, z);
@@ -53,8 +59,10 @@ public class PacketBlockConnectionProvider extends BlockConnectionProvider {
}
@Override
public boolean storesBlocks() {
return true;
public boolean storesBlocks(UserConnection connection, @Nullable Position pos) {
if (pos == null || connection == null) return true;
return !connection.get(BlockConnectionStorage.class).recentlyModified(pos);
}
@Override
@@ -72,7 +72,7 @@ public class InventoryPackets extends ItemRewriter<ClientboundPackets1_12_1, Ser
map(Type.UNSIGNED_BYTE); // 0 - Window ID
map(Type.ITEM_ARRAY, Type.FLAT_ITEM_ARRAY); // 1 - Window Values
handler(itemArrayHandler(Type.FLAT_ITEM_ARRAY));
handler(itemArrayToClientHandler(Type.FLAT_ITEM_ARRAY));
}
});
protocol.registerClientbound(ClientboundPackets1_12_1.WINDOW_PROPERTY, new PacketHandlers() {
@@ -36,6 +36,7 @@ import com.viaversion.viaversion.api.type.types.Particle;
import com.viaversion.viaversion.protocols.protocol1_12_1to1_12.ClientboundPackets1_12_1;
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_13to1_12_2.ServerboundPackets1_13;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.ConnectionData;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.ConnectionHandler;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.data.NamedSoundRewriter;
@@ -44,10 +45,11 @@ import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.providers.BlockE
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.providers.PaintingProvider;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.types.Chunk1_13Type;
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.types.Chunk1_9_3_4Type;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
@@ -56,7 +58,7 @@ public class WorldPackets {
private static final IntSet VALID_BIOMES = new IntOpenHashSet(70, .99F);
static {
// Client will crash if it receives a invalid biome id
// Client will crash if it receives an invalid biome id
for (int i = 0; i < 50; i++) {
VALID_BIOMES.add(i);
}
@@ -535,6 +537,32 @@ public class WorldPackets {
});
}
});
// Incoming Packets
protocol.registerServerbound(ServerboundPackets1_13.PLAYER_BLOCK_PLACEMENT, wrapper -> {
Position pos = wrapper.passthrough(Type.POSITION);
wrapper.passthrough(Type.VAR_INT); // block face
wrapper.passthrough(Type.VAR_INT); // hand
wrapper.passthrough(Type.FLOAT); // cursor x
wrapper.passthrough(Type.FLOAT); // cursor y
wrapper.passthrough(Type.FLOAT); // cursor z
if (Via.getConfig().isServersideBlockConnections() && ConnectionData.needStoreBlocks()) {
ConnectionData.markModified(wrapper.user(), pos);
}
});
protocol.registerServerbound(ServerboundPackets1_13.PLAYER_DIGGING, wrapper -> {
int status = wrapper.passthrough(Type.VAR_INT); // Status
Position pos = wrapper.passthrough(Type.POSITION); // Location
wrapper.passthrough(Type.UNSIGNED_BYTE); // block face
// 0 = Started digging: if in creative this causes the block to break directly
// There's no point in storing the finished digging as it may never show-up (creative)
if (status == 0 && Via.getConfig().isServersideBlockConnections() && ConnectionData.needStoreBlocks()) {
ConnectionData.markModified(wrapper.user(), pos);
}
});
}
public static int toNewId(int oldId) {
@@ -17,21 +17,28 @@
*/
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage;
import com.google.common.collect.EvictingQueue;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.StorableObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import com.viaversion.viaversion.api.minecraft.Position;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BlockConnectionStorage implements StorableObject {
private static Constructor<?> fastUtilLongObjectHashMap;
private final Map<Long, SectionData> blockStorage = createLongObjectMap();
@SuppressWarnings("UnstableApiUsage")
private final Queue<Position> modified = EvictingQueue.create(5);
// Cache to retrieve section quicker
private Long lastIndex;
private long lastIndex = -1;
private SectionData lastSection;
static {
@@ -88,10 +95,27 @@ public class BlockConnectionStorage implements StorableObject {
}
}
public void markModified(Position pos) {
// Avoid saving the same pos twice
if (!modified.contains(pos)) {
this.modified.add(pos);
}
}
public boolean recentlyModified(Position pos) {
for (Position p : modified) {
if (Math.abs(pos.x() - p.x()) + Math.abs(pos.y() - p.y()) + Math.abs(pos.z() - p.z()) <= 2) {
return true;
}
}
return false;
}
public void clear() {
blockStorage.clear();
lastSection = null;
lastIndex = null;
lastIndex = -1;
modified.clear();
}
public void unloadChunk(int x, int z) {
@@ -105,7 +129,7 @@ public class BlockConnectionStorage implements StorableObject {
}
private @Nullable SectionData getSection(long index) {
if (lastIndex != null && lastIndex == index) {
if (lastIndex == index) {
return lastSection;
}
lastIndex = index;
@@ -114,8 +138,8 @@ public class BlockConnectionStorage implements StorableObject {
private void removeSection(long index) {
blockStorage.remove(index);
if (lastIndex != null && lastIndex == index) {
lastIndex = null;
if (lastIndex == index) {
lastIndex = -1;
lastSection = null;
}
}
@@ -17,6 +17,9 @@
*/
package com.viaversion.viaversion.protocols.protocol1_17to1_16_4.packets;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
@@ -122,4 +125,23 @@ public final class InventoryPackets extends ItemRewriter<ClientboundPackets1_16_
wrapper.cancel();
});
}
@Override
public Item handleItemToClient(Item item) {
if (item == null) return null;
CompoundTag tag = item.tag();
if (item.identifier() == 733) {
if (tag == null) {
item.setTag(tag = new CompoundTag());
}
if (!(tag.get("map") instanceof NumberTag)) {
tag.put("map", new IntTag(0));
}
}
item.setIdentifier(this.protocol.getMappingData().getNewItemId(item.identifier()));
return item;
}
}
@@ -48,7 +48,7 @@ public class RecipeRewriter1_19_3<C extends ClientboundPacketType> extends Recip
wrapper.passthrough(Type.STRING); // Group
wrapper.passthrough(Type.VAR_INT); // Crafting book category
handleIngredients(wrapper);
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
}
@Override
@@ -59,7 +59,7 @@ public class RecipeRewriter1_19_3<C extends ClientboundPacketType> extends Recip
for (int i = 0; i < ingredients; i++) {
handleIngredient(wrapper);
}
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
}
@Override
@@ -67,7 +67,7 @@ public class RecipeRewriter1_19_3<C extends ClientboundPacketType> extends Recip
wrapper.passthrough(Type.STRING); // Group
wrapper.passthrough(Type.VAR_INT); // Crafting book category
handleIngredient(wrapper);
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
wrapper.passthrough(Type.FLOAT); // EXP
wrapper.passthrough(Type.VAR_INT); // Cooking time
}
@@ -64,6 +64,14 @@ public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPack
return element == null || element.isJsonNull() || (element.isJsonArray() && element.getAsJsonArray().size() == 0);
}
public static JsonElement mapTextComponentIfNull(JsonElement component) {
if (!isTextComponentNull(component)) {
return component;
} else {
return ChatRewriter.emptyComponent();
}
}
@Override
protected void registerPackets() {
final TagRewriter<ClientboundPackets1_18> tagRewriter = new TagRewriter<>(this);
@@ -118,16 +126,32 @@ public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPack
new StatisticsRewriter<>(this).register(ClientboundPackets1_18.STATISTICS);
final PacketHandler titleHandler = wrapper -> {
final JsonElement component = wrapper.read(Type.COMPONENT);
if (!isTextComponentNull(component)) {
wrapper.write(Type.COMPONENT, component);
} else {
wrapper.write(Type.COMPONENT, ChatRewriter.emptyComponent());
}
final PacketHandler singleNullTextComponentMapper = wrapper -> {
wrapper.write(Type.COMPONENT, mapTextComponentIfNull(wrapper.read(Type.COMPONENT)));
};
registerClientbound(ClientboundPackets1_18.TITLE_TEXT, titleHandler);
registerClientbound(ClientboundPackets1_18.TITLE_SUBTITLE, titleHandler);
registerClientbound(ClientboundPackets1_18.TITLE_TEXT, singleNullTextComponentMapper);
registerClientbound(ClientboundPackets1_18.TITLE_SUBTITLE, singleNullTextComponentMapper);
registerClientbound(ClientboundPackets1_18.ACTIONBAR, singleNullTextComponentMapper);
registerClientbound(ClientboundPackets1_18.SCOREBOARD_OBJECTIVE, wrapper -> {
wrapper.passthrough(Type.STRING); // Objective Name
byte action = wrapper.passthrough(Type.BYTE); // Mode
if (action == 0 || action == 2) {
wrapper.write(Type.COMPONENT, mapTextComponentIfNull(wrapper.read(Type.COMPONENT))); // Display Name
}
});
registerClientbound(ClientboundPackets1_18.TEAMS, wrapper -> {
wrapper.passthrough(Type.STRING); // Team Name
byte action = wrapper.passthrough(Type.BYTE); // Mode
if (action == 0 || action == 2) {
wrapper.write(Type.COMPONENT, mapTextComponentIfNull(wrapper.read(Type.COMPONENT))); // Display Name
wrapper.passthrough(Type.BYTE); // Flags
wrapper.passthrough(Type.STRING); // Name Tag Visibility
wrapper.passthrough(Type.STRING); // Collision rule
wrapper.passthrough(Type.VAR_INT); // Color
wrapper.write(Type.COMPONENT, mapTextComponentIfNull(wrapper.read(Type.COMPONENT))); // Prefix
wrapper.write(Type.COMPONENT, mapTextComponentIfNull(wrapper.read(Type.COMPONENT))); // Suffix
}
});
final CommandRewriter<ClientboundPackets1_18> commandRewriter = new CommandRewriter<>(this);
registerClientbound(ClientboundPackets1_18.DECLARE_COMMANDS, wrapper -> {
@@ -69,7 +69,7 @@ public final class InventoryPackets extends ItemRewriter<ClientboundPackets1_18,
}
}
});
handler(getSpawnParticleHandler(Type.VAR_INT, Type.FLAT_VAR_INT_ITEM));
handler(getSpawnParticleHandler(Type.VAR_INT));
}
});
@@ -44,8 +44,8 @@ public final class WorldPackets {
protocol.registerClientbound(ClientboundPackets1_18.CHUNK_DATA, 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");
Preconditions.checkArgument(tracker.biomesSent() != -1, "Biome count not set");
Preconditions.checkArgument(tracker.currentWorldSectionHeight() != -1, "Section height not set");
final Chunk1_18Type chunkType = new Chunk1_18Type(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),
MathUtil.ceilLog2(tracker.biomesSent()));
@@ -29,6 +29,7 @@ public class AckSequenceProvider implements Provider {
public void handleSequence(final UserConnection connection, final int sequence) throws Exception {
final PacketWrapper ackPacket = PacketWrapper.create(ClientboundPackets1_19.BLOCK_CHANGED_ACK, connection);
ackPacket.write(Type.VAR_INT, sequence);
ackPacket.scheduleSend(Protocol1_19To1_18_2.class);
ackPacket.send(Protocol1_19To1_18_2.class);
}
}
}
@@ -0,0 +1,330 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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_20_2to1_20;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
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.entities.Entity1_19_4Types;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
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.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.ClientboundPackets1_19_4;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.ServerboundPackets1_19_4;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ClientboundConfigurationPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ClientboundPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ServerboundConfigurationPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ServerboundPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.rewriter.BlockItemPacketRewriter1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.rewriter.EntityPacketRewriter1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.storage.ConfigurationState;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.storage.ConfigurationState.BridgePhase;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.storage.LastResourcePack;
import com.viaversion.viaversion.rewriter.SoundRewriter;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPackets1_19_4, ClientboundPackets1_20_2, ServerboundPackets1_19_4, ServerboundPackets1_20_2> {
public static final MappingData MAPPINGS = new MappingDataBase("1.20", "1.20.2");
private final EntityPacketRewriter1_20_2 entityPacketRewriter = new EntityPacketRewriter1_20_2(this);
private final BlockItemPacketRewriter1_20_2 itemPacketRewriter = new BlockItemPacketRewriter1_20_2(this);
public Protocol1_20_2To1_20() {
super(ClientboundPackets1_19_4.class, ClientboundPackets1_20_2.class, ServerboundPackets1_19_4.class, ServerboundPackets1_20_2.class);
}
@Override
protected void registerPackets() {
// Close your eyes and turn around while you still can
super.registerPackets();
final SoundRewriter<ClientboundPackets1_19_4> soundRewriter = new SoundRewriter<>(this);
soundRewriter.register1_19_3Sound(ClientboundPackets1_19_4.SOUND);
soundRewriter.registerEntitySound(ClientboundPackets1_19_4.ENTITY_SOUND);
registerClientbound(ClientboundPackets1_19_4.PLUGIN_MESSAGE, wrapper -> {
final String channel = wrapper.passthrough(Type.STRING);
if (channel.equals("minecraft:brand")) {
wrapper.passthrough(Type.STRING);
wrapper.read(Type.REMAINING_BYTES);
}
});
registerServerbound(ServerboundPackets1_20_2.PLUGIN_MESSAGE, wrapper -> {
final String channel = wrapper.passthrough(Type.STRING);
if (channel.equals("minecraft:brand")) {
wrapper.passthrough(Type.STRING);
wrapper.read(Type.REMAINING_BYTES);
}
});
registerClientbound(ClientboundPackets1_19_4.RESOURCE_PACK, wrapper -> {
final String url = wrapper.passthrough(Type.STRING);
final String hash = wrapper.passthrough(Type.STRING);
final boolean required = wrapper.passthrough(Type.BOOLEAN);
final JsonElement prompt = wrapper.passthrough(Type.OPTIONAL_COMPONENT);
wrapper.user().put(new LastResourcePack(url, hash, required, prompt));
});
registerClientbound(ClientboundPackets1_19_4.DISPLAY_SCOREBOARD, wrapper -> {
final byte slot = wrapper.read(Type.BYTE);
wrapper.write(Type.VAR_INT, (int) slot);
});
registerServerbound(State.LOGIN, ServerboundLoginPackets.HELLO.getId(), ServerboundLoginPackets.HELLO.getId(), wrapper -> {
wrapper.passthrough(Type.STRING); // Name
final UUID uuid = wrapper.read(Type.UUID);
wrapper.write(Type.OPTIONAL_UUID, uuid);
});
// Deal with the new CONFIGURATION protocol state the client expects
// After the game profile is received by the client, it will send its login ack,
// switch to the configuration protocol state and send its brand.
// We need to wait for it send the login ack before actually sending the play login,
// hence packets are added to a queue. With the data from the login packet, we sent what is needed
// during the configuration phase before finally transitioning to the play state with the client as well.
registerClientbound(State.LOGIN, ClientboundLoginPackets.GAME_PROFILE.getId(), ClientboundLoginPackets.GAME_PROFILE.getId(), wrapper -> {
wrapper.user().get(ConfigurationState.class).setBridgePhase(BridgePhase.PROFILE_SENT);
wrapper.user().getProtocolInfo().setServerState(State.PLAY);
});
registerServerbound(State.LOGIN, ServerboundLoginPackets.LOGIN_ACKNOWLEDGED.getId(), -1, wrapper -> {
wrapper.cancel();
// Overwrite the state set in the base protocol to what the server actually keeps sending
wrapper.user().getProtocolInfo().setServerState(State.PLAY);
final ConfigurationState configurationState = wrapper.user().get(ConfigurationState.class);
configurationState.setBridgePhase(BridgePhase.CONFIGURATION);
configurationState.sendQueuedPackets(wrapper.user());
});
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.FINISH_CONFIGURATION.getId(), -1, wrapper -> {
wrapper.cancel();
wrapper.user().getProtocolInfo().setClientState(State.PLAY);
final ConfigurationState configurationState = wrapper.user().get(ConfigurationState.class);
configurationState.setBridgePhase(BridgePhase.NONE);
configurationState.sendQueuedPackets(wrapper.user());
configurationState.clear();
});
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.CLIENT_INFORMATION.getId(), -1, wrapper -> {
final ConfigurationState.ClientInformation clientInformation = new ConfigurationState.ClientInformation(
wrapper.read(Type.STRING), // Language
wrapper.read(Type.BYTE), // View distance
wrapper.read(Type.VAR_INT), // Chat visibility
wrapper.read(Type.BOOLEAN), // Chat colors
wrapper.read(Type.UNSIGNED_BYTE), // Model customization
wrapper.read(Type.VAR_INT), // Main hand
wrapper.read(Type.BOOLEAN), // Text filtering enabled
wrapper.read(Type.BOOLEAN) // Allow listing in server list preview
);
// Store it to re-send it when another ClientboundLoginPacket is sent, since the client will only send it
// once per connection right after the handshake
final ConfigurationState configurationState = wrapper.user().get(ConfigurationState.class);
configurationState.setClientInformation(clientInformation);
wrapper.cancel();
});
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.CUSTOM_PAYLOAD.getId(), -1, queueServerboundPacket(ServerboundPackets1_20_2.PLUGIN_MESSAGE));
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.KEEP_ALIVE.getId(), -1, queueServerboundPacket(ServerboundPackets1_20_2.KEEP_ALIVE));
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.PONG.getId(), -1, queueServerboundPacket(ServerboundPackets1_20_2.PONG));
// Cancel this, as it will always just be the response to a re-sent pack from us
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.RESOURCE_PACK.getId(), -1, PacketWrapper::cancel);
cancelClientbound(ClientboundPackets1_19_4.UPDATE_ENABLED_FEATURES); // TODO Sad emoji
registerServerbound(ServerboundPackets1_20_2.CONFIGURATION_ACKNOWLEDGED, null, wrapper -> {
wrapper.cancel();
final ConfigurationState configurationState = wrapper.user().get(ConfigurationState.class);
if (configurationState.bridgePhase() != BridgePhase.REENTERING_CONFIGURATION) {
return;
}
// Reenter the configuration state
wrapper.user().getProtocolInfo().setClientState(State.CONFIGURATION);
configurationState.setBridgePhase(BridgePhase.CONFIGURATION);
final LastResourcePack lastResourcePack = wrapper.user().get(LastResourcePack.class);
sendConfigurationPackets(wrapper.user(), configurationState.lastDimensionRegistry(), lastResourcePack);
});
cancelServerbound(ServerboundPackets1_20_2.CHUNK_BATCH_RECEIVED);
registerServerbound(ServerboundPackets1_20_2.PING_REQUEST, null, wrapper -> {
wrapper.cancel();
final long time = wrapper.read(Type.LONG);
final PacketWrapper responsePacket = wrapper.create(ClientboundPackets1_20_2.PONG_RESPONSE);
responsePacket.write(Type.LONG, time);
responsePacket.sendFuture(Protocol1_20_2To1_20.class);
});
}
private PacketHandler queueServerboundPacket(final ServerboundPackets1_20_2 packetType) {
return wrapper -> {
wrapper.setPacketType(packetType);
wrapper.user().get(ConfigurationState.class).addPacketToQueue(wrapper, false);
wrapper.cancel();
};
}
@Override
public void transform(final Direction direction, final State state, final PacketWrapper packetWrapper) throws Exception {
if (direction == Direction.SERVERBOUND) {
// The client will have the correct state set
super.transform(direction, state, packetWrapper);
return;
}
final ConfigurationState configurationBridge = packetWrapper.user().get(ConfigurationState.class);
if (configurationBridge == null) {
// Bad state during an unexpected disconnect
return;
}
final BridgePhase phase = configurationBridge.bridgePhase();
if (phase == BridgePhase.NONE) {
super.transform(direction, state, packetWrapper);
return;
}
if (phase == BridgePhase.PROFILE_SENT || phase == BridgePhase.REENTERING_CONFIGURATION) {
// Queue packets sent by the server while we wait for the client to transition to the configuration state
configurationBridge.addPacketToQueue(packetWrapper, true);
throw CancelException.generate();
}
if (packetWrapper.getPacketType() == null || packetWrapper.getPacketType().state() != State.CONFIGURATION) {
// Map some of them to their configuration state counterparts, but make sure to let join game through
final int unmappedId = packetWrapper.getId();
if (unmappedId == ClientboundPackets1_19_4.JOIN_GAME.getId()) {
super.transform(direction, State.PLAY, packetWrapper);
return;
}
if (configurationBridge.queuedOrSentJoinGame()) {
if (!packetWrapper.user().isClientSide() && !Via.getPlatform().isProxy() && unmappedId == ClientboundPackets1_19_4.SYSTEM_CHAT.getId()) {
// Cancelling this on the Vanilla server will cause it to exceptionally resend a message
// Assume that we have already sent the login packet and just let it through
super.transform(direction, State.PLAY, packetWrapper);
return;
}
configurationBridge.addPacketToQueue(packetWrapper, true);
throw CancelException.generate();
}
if (unmappedId == ClientboundPackets1_19_4.PLUGIN_MESSAGE.getId()) {
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.CUSTOM_PAYLOAD);
} else if (unmappedId == ClientboundPackets1_19_4.DISCONNECT.getId()) {
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.DISCONNECT);
} else if (unmappedId == ClientboundPackets1_19_4.KEEP_ALIVE.getId()) {
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.KEEP_ALIVE);
} else if (unmappedId == ClientboundPackets1_19_4.PING.getId()) {
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.PING);
} else if (unmappedId == ClientboundPackets1_19_4.UPDATE_ENABLED_FEATURES.getId()) {
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.UPDATE_ENABLED_FEATURES);
} else if (unmappedId == ClientboundPackets1_19_4.TAGS.getId()) {
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.UPDATE_TAGS);
} else {
// Not a packet that can be mapped to the configuration protocol
// Includes resource pack packets to make sure it is not applied sooner than the server expects
configurationBridge.addPacketToQueue(packetWrapper, true);
throw CancelException.generate();
}
return;
}
// Redirect packets during the fake configuration phase
// This might mess up people using Via API/other protocols down the line, but such is life. We can't have different states for server and client
super.transform(direction, State.CONFIGURATION, packetWrapper);
}
public static void sendConfigurationPackets(final UserConnection connection, final CompoundTag dimensionRegistry, @Nullable final LastResourcePack lastResourcePack) throws Exception {
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
protocolInfo.setServerState(State.CONFIGURATION);
final PacketWrapper registryDataPacket = PacketWrapper.create(ClientboundConfigurationPackets1_20_2.REGISTRY_DATA, connection);
registryDataPacket.write(Type.NAMELESS_NBT, dimensionRegistry);
registryDataPacket.send(Protocol1_20_2To1_20.class);
// Enabling features is only possible during the configuration phase
// TODO Sad emoji
final PacketWrapper enableFeaturesPacket = PacketWrapper.create(ClientboundConfigurationPackets1_20_2.UPDATE_ENABLED_FEATURES, connection);
enableFeaturesPacket.write(Type.VAR_INT, 1);
enableFeaturesPacket.write(Type.STRING, "minecraft:vanilla");
enableFeaturesPacket.send(Protocol1_20_2To1_20.class);
if (lastResourcePack != null) {
// The client for some reason drops the resource pack when reentering the configuration state
final PacketWrapper resourcePackPacket = PacketWrapper.create(ClientboundConfigurationPackets1_20_2.RESOURCE_PACK, connection);
resourcePackPacket.write(Type.STRING, lastResourcePack.url());
resourcePackPacket.write(Type.STRING, lastResourcePack.hash());
resourcePackPacket.write(Type.BOOLEAN, lastResourcePack.required());
resourcePackPacket.write(Type.OPTIONAL_COMPONENT, lastResourcePack.prompt());
resourcePackPacket.send(Protocol1_20_2To1_20.class);
}
final PacketWrapper finishConfigurationPacket = PacketWrapper.create(ClientboundConfigurationPackets1_20_2.FINISH_CONFIGURATION, connection);
finishConfigurationPacket.send(Protocol1_20_2To1_20.class);
protocolInfo.setServerState(State.PLAY);
}
@Override
public MappingData getMappingData() {
return MAPPINGS;
}
@Override
protected void registerConfigurationChangeHandlers() {
// Don't handle it in the transitioning protocol
}
@Override
public void init(final UserConnection user) {
user.put(new ConfigurationState());
addEntityTracker(user, new EntityTrackerBase(user, Entity1_19_4Types.PLAYER));
}
@Override
public EntityRewriter<Protocol1_20_2To1_20> getEntityRewriter() {
return entityPacketRewriter;
}
@Override
public ItemRewriter<Protocol1_20_2To1_20> getItemRewriter() {
return itemPacketRewriter;
}
}
@@ -0,0 +1,49 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_20_2to1_20.packet;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.State;
public enum ClientboundConfigurationPackets1_20_2 implements ClientboundPacketType {
CUSTOM_PAYLOAD, // 0x00
DISCONNECT, // 0x01
FINISH_CONFIGURATION, // 0x02
KEEP_ALIVE, // 0x03
PING, // 0x04
REGISTRY_DATA, // 0x05
RESOURCE_PACK, // 0x06
UPDATE_ENABLED_FEATURES, // 0x07
UPDATE_TAGS; // 0x08
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
@Override
public State state() {
return State.CONFIGURATION;
}
}
@@ -0,0 +1,147 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_20_2to1_20.packet;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
public enum ClientboundPackets1_20_2 implements ClientboundPacketType {
BUNDLE, // 0x00
SPAWN_ENTITY, // 0x01
SPAWN_EXPERIENCE_ORB, // 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
CHUNK_BATCH_FINISHED, // 0x0C
CHUNK_BATCH_START, // 0x0D
CHUNK_BIOMES, // 0x0E
CLEAR_TITLES, // 0x0F
TAB_COMPLETE, // 0x10
DECLARE_COMMANDS, // 0x11
CLOSE_WINDOW, // 0x12
WINDOW_ITEMS, // 0x13
WINDOW_PROPERTY, // 0x14
SET_SLOT, // 0x15
COOLDOWN, // 0x16
CUSTOM_CHAT_COMPLETIONS, // 0x17
PLUGIN_MESSAGE, // 0x18
DAMAGE_EVENT, // 0x19
DELETE_CHAT_MESSAGE, // 0x1A
DISCONNECT, // 0x1B
DISGUISED_CHAT, // 0x1C
ENTITY_STATUS, // 0x1D
EXPLOSION, // 0x1E
UNLOAD_CHUNK, // 0x1F
GAME_EVENT, // 0x20
OPEN_HORSE_WINDOW, // 0x21
HIT_ANIMATION, // 0x22
WORLD_BORDER_INIT, // 0x23
KEEP_ALIVE, // 0x24
CHUNK_DATA, // 0x25
EFFECT, // 0x26
SPAWN_PARTICLE, // 0x27
UPDATE_LIGHT, // 0x28
JOIN_GAME, // 0x29
MAP_DATA, // 0x2A
TRADE_LIST, // 0x2B
ENTITY_POSITION, // 0x2C
ENTITY_POSITION_AND_ROTATION, // 0x2D
ENTITY_ROTATION, // 0x2E
VEHICLE_MOVE, // 0x2F
OPEN_BOOK, // 0x30
OPEN_WINDOW, // 0x31
OPEN_SIGN_EDITOR, // 0x32
PING, // 0x33
PONG_RESPONSE, // 0x34
CRAFT_RECIPE_RESPONSE, // 0x35
PLAYER_ABILITIES, // 0x36
PLAYER_CHAT, // 0x37
COMBAT_END, // 0x38
COMBAT_ENTER, // 0x39
COMBAT_KILL, // 0x3A
PLAYER_INFO_REMOVE, // 0x3B
PLAYER_INFO_UPDATE, // 0x3C
FACE_PLAYER, // 0x3D
PLAYER_POSITION, // 0x3E
UNLOCK_RECIPES, // 0x3F
REMOVE_ENTITIES, // 0x40
REMOVE_ENTITY_EFFECT, // 0x41
RESOURCE_PACK, // 0x42
RESPAWN, // 0x43
ENTITY_HEAD_LOOK, // 0x44
MULTI_BLOCK_CHANGE, // 0x45
SELECT_ADVANCEMENTS_TAB, // 0x46
SERVER_DATA, // 0x47
ACTIONBAR, // 0x48
WORLD_BORDER_CENTER, // 0x49
WORLD_BORDER_LERP_SIZE, // 0x4A
WORLD_BORDER_SIZE, // 0x4B
WORLD_BORDER_WARNING_DELAY, // 0x4C
WORLD_BORDER_WARNING_DISTANCE, // 0x4D
CAMERA, // 0x4E
HELD_ITEM_CHANGE, // 0x4F
UPDATE_VIEW_POSITION, // 0x50
UPDATE_VIEW_DISTANCE, // 0x51
SPAWN_POSITION, // 0x52
DISPLAY_SCOREBOARD, // 0x53
ENTITY_METADATA, // 0x54
ATTACH_ENTITY, // 0x55
ENTITY_VELOCITY, // 0x56
ENTITY_EQUIPMENT, // 0x57
SET_EXPERIENCE, // 0x58
UPDATE_HEALTH, // 0x59
SCOREBOARD_OBJECTIVE, // 0x5A
SET_PASSENGERS, // 0x5B
TEAMS, // 0x5C
UPDATE_SCORE, // 0x5D
SET_SIMULATION_DISTANCE, // 0x5E
TITLE_SUBTITLE, // 0x5F
TIME_UPDATE, // 0x60
TITLE_TEXT, // 0x61
TITLE_TIMES, // 0x62
ENTITY_SOUND, // 0x63
SOUND, // 0x64
START_CONFIGURATION, // 0x65
STOP_SOUND, // 0x66
SYSTEM_CHAT, // 0x67
TAB_LIST, // 0x68
NBT_QUERY, // 0x69
COLLECT_ITEM, // 0x6A
ENTITY_TELEPORT, // 0x6B
ADVANCEMENTS, // 0x6C
ENTITY_PROPERTIES, // 0x6D
ENTITY_EFFECT, // 0x6E
DECLARE_RECIPES, // 0x6F
TAGS; // 0x70
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
}
@@ -0,0 +1,46 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_20_2to1_20.packet;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.State;
public enum ServerboundConfigurationPackets1_20_2 implements ServerboundPacketType {
CLIENT_INFORMATION, // 0x00
CUSTOM_PAYLOAD, // 0x01
FINISH_CONFIGURATION, // 0x02
KEEP_ALIVE, // 0x03
PONG, // 0x04
RESOURCE_PACK; // 0x05
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
@Override
public State state() {
return State.CONFIGURATION;
}
}
@@ -0,0 +1,88 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_20_2to1_20.packet;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
public enum ServerboundPackets1_20_2 implements ServerboundPacketType {
TELEPORT_CONFIRM, // 0x00
QUERY_BLOCK_NBT, // 0x01
SET_DIFFICULTY, // 0x02
CHAT_ACK, // 0x03
CHAT_COMMAND, // 0x04
CHAT_MESSAGE, // 0x05
CHAT_SESSION_UPDATE, // 0x06
CHUNK_BATCH_RECEIVED, // 0x07
CLIENT_STATUS, // 0x08
CLIENT_SETTINGS, // 0x09
TAB_COMPLETE, // 0x0A
CONFIGURATION_ACKNOWLEDGED, // 0x0B
CLICK_WINDOW_BUTTON, // 0x0C
CLICK_WINDOW, // 0x0D
CLOSE_WINDOW, // 0x0E
PLUGIN_MESSAGE, // 0x0F
EDIT_BOOK, // 0x10
ENTITY_NBT_REQUEST, // 0x11
INTERACT_ENTITY, // 0x12
GENERATE_JIGSAW, // 0x13
KEEP_ALIVE, // 0x14
LOCK_DIFFICULTY, // 0x15
PLAYER_POSITION, // 0x16
PLAYER_POSITION_AND_ROTATION, // 0x17
PLAYER_ROTATION, // 0x18
PLAYER_MOVEMENT, // 0x19
VEHICLE_MOVE, // 0x1A
STEER_BOAT, // 0x1B
PICK_ITEM, // 0x1C
PING_REQUEST, // 0x1D
CRAFT_RECIPE_REQUEST, // 0x1E
PLAYER_ABILITIES, // 0x1F
PLAYER_DIGGING, // 0x20
ENTITY_ACTION, // 0x21
STEER_VEHICLE, // 0x22
PONG, // 0x23
RECIPE_BOOK_DATA, // 0x24
SEEN_RECIPE, // 0x25
RENAME_ITEM, // 0x26
RESOURCE_PACK_STATUS, // 0x27
ADVANCEMENT_TAB, // 0x28
SELECT_TRADE, // 0x29
SET_BEACON_EFFECT, // 0x2A
HELD_ITEM_CHANGE, // 0x2B
UPDATE_COMMAND_BLOCK, // 0x2C
UPDATE_COMMAND_BLOCK_MINECART, // 0x2D
CREATIVE_INVENTORY_ACTION, // 0x2E
UPDATE_JIGSAW_BLOCK, // 0x2F
UPDATE_STRUCTURE_BLOCK, // 0x30
UPDATE_SIGN, // 0x31
ANIMATION, // 0x32
SPECTATE, // 0x33
PLAYER_BLOCK_PLACEMENT, // 0x34
USE_ITEM; // 0x35
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
}
@@ -0,0 +1,455 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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_20_2to1_20.rewriter;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
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.data.ParticleMappings;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
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.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.metadata.ChunkPosition;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.types.Chunk1_18Type;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.ClientboundPackets1_19_4;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.rewriter.RecipeRewriter1_19_4;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.Protocol1_20_2To1_20;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ServerboundPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.type.ChunkType1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.util.PotionEffects;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.ItemRewriter;
import com.viaversion.viaversion.util.MathUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<ClientboundPackets1_19_4, ServerboundPackets1_20_2, Protocol1_20_2To1_20> {
public BlockItemPacketRewriter1_20_2(final Protocol1_20_2To1_20 protocol) {
super(protocol);
}
@Override
public void registerPackets() {
final BlockRewriter<ClientboundPackets1_19_4> blockRewriter = new BlockRewriter<>(protocol, Type.POSITION1_14);
blockRewriter.registerBlockAction(ClientboundPackets1_19_4.BLOCK_ACTION);
blockRewriter.registerBlockChange(ClientboundPackets1_19_4.BLOCK_CHANGE);
blockRewriter.registerVarLongMultiBlockChange1_20(ClientboundPackets1_19_4.MULTI_BLOCK_CHANGE);
blockRewriter.registerEffect(ClientboundPackets1_19_4.EFFECT, 1010, 2001);
protocol.registerServerbound(ServerboundPackets1_20_2.SET_BEACON_EFFECT, wrapper -> {
// Effects start at 1 before 1.20.2
if (wrapper.passthrough(Type.BOOLEAN)) { // Primary effect
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) + 1);
}
if (wrapper.passthrough(Type.BOOLEAN)) { // Secondary effect
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) + 1);
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.UNLOAD_CHUNK, wrapper -> {
final int x = wrapper.read(Type.INT);
final int z = wrapper.read(Type.INT);
wrapper.write(Type.CHUNK_POSITION, new ChunkPosition(x, z));
});
protocol.registerClientbound(ClientboundPackets1_19_4.NBT_QUERY, wrapper -> {
wrapper.passthrough(Type.VAR_INT); // Transaction id
wrapper.write(Type.NAMELESS_NBT, wrapper.read(Type.NBT));
});
protocol.registerClientbound(ClientboundPackets1_19_4.BLOCK_ENTITY_DATA, wrapper -> {
wrapper.passthrough(Type.POSITION1_14); // Position
wrapper.passthrough(Type.VAR_INT); // Type
wrapper.write(Type.NAMELESS_NBT, handleBlockEntity(wrapper.read(Type.NBT)));
});
protocol.registerClientbound(ClientboundPackets1_19_4.CHUNK_DATA, wrapper -> {
final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());
final Type<Chunk> chunkType = new Chunk1_18Type(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().size()),
MathUtil.ceilLog2(tracker.biomesSent()));
final Chunk chunk = wrapper.read(chunkType);
final Type<Chunk> newChunkType = new ChunkType1_20_2(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),
MathUtil.ceilLog2(tracker.biomesSent()));
wrapper.write(newChunkType, chunk);
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));
}
}
for (final BlockEntity blockEntity : chunk.blockEntities()) {
handleBlockEntity(blockEntity.tag());
}
});
// Replace the NBT type everywhere
protocol.registerClientbound(ClientboundPackets1_19_4.WINDOW_ITEMS, new PacketHandlers() {
@Override
public void register() {
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
handler(wrapper -> {
final Item[] items = wrapper.read(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT);
for (final Item item : items) {
handleItemToClient(item);
}
wrapper.write(Type.ITEM1_20_2_VAR_INT_ARRAY, items);
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Carried item
});
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.SET_SLOT, new PacketHandlers() {
@Override
public void register() {
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
map(Type.SHORT); // Slot id
handler(wrapper -> wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))));
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.ADVANCEMENTS, wrapper -> {
wrapper.passthrough(Type.BOOLEAN); // Reset/clear
final int size = wrapper.passthrough(Type.VAR_INT); // Mapping size
for (int i = 0; i < size; i++) {
wrapper.passthrough(Type.STRING); // Identifier
// Parent
if (wrapper.passthrough(Type.BOOLEAN))
wrapper.passthrough(Type.STRING);
// Display data
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.passthrough(Type.COMPONENT); // Title
wrapper.passthrough(Type.COMPONENT); // Description
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Icon
wrapper.passthrough(Type.VAR_INT); // Frame type
final int flags = wrapper.passthrough(Type.INT); // Flags
if ((flags & 1) != 0) {
wrapper.passthrough(Type.STRING); // Background texture
}
wrapper.passthrough(Type.FLOAT); // X
wrapper.passthrough(Type.FLOAT); // Y
}
// Remove criterion triggers
wrapper.read(Type.STRING_ARRAY); // Criteria
final int requirements = wrapper.passthrough(Type.VAR_INT);
for (int array = 0; array < requirements; array++) {
wrapper.passthrough(Type.STRING_ARRAY);
}
wrapper.passthrough(Type.BOOLEAN); // Send telemetry
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.ENTITY_EQUIPMENT, new PacketHandlers() {
@Override
public void register() {
map(Type.VAR_INT); // 0 - Entity ID
handler(wrapper -> {
byte slot;
do {
slot = wrapper.passthrough(Type.BYTE);
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM)));
} while ((slot & 0xFFFFFF80) != 0);
});
}
});
protocol.registerServerbound(ServerboundPackets1_20_2.CLICK_WINDOW, new PacketHandlers() {
@Override
public void register() {
map(Type.UNSIGNED_BYTE); // Window Id
map(Type.VAR_INT); // State id
map(Type.SHORT); // Slot
map(Type.BYTE); // Button
map(Type.VAR_INT); // Mode
handler(wrapper -> {
// Affected items
final int length = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < length; i++) {
wrapper.passthrough(Type.SHORT); // Slot
wrapper.write(Type.FLAT_VAR_INT_ITEM, handleItemToServer(wrapper.read(Type.ITEM1_20_2)));
}
// Carried item
wrapper.write(Type.FLAT_VAR_INT_ITEM, handleItemToServer(wrapper.read(Type.ITEM1_20_2)));
});
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.TRADE_LIST, wrapper -> {
wrapper.passthrough(Type.VAR_INT); // Container id
final int size = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < size; i++) {
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Input
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Output
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Second Item
wrapper.passthrough(Type.BOOLEAN); // Trade disabled
wrapper.passthrough(Type.INT); // Number of tools uses
wrapper.passthrough(Type.INT); // Maximum number of trade uses
wrapper.passthrough(Type.INT); // XP
wrapper.passthrough(Type.INT); // Special price
wrapper.passthrough(Type.FLOAT); // Price multiplier
wrapper.passthrough(Type.INT); // Demand
}
});
protocol.registerServerbound(ServerboundPackets1_20_2.CREATIVE_INVENTORY_ACTION, new PacketHandlers() {
@Override
public void register() {
map(Type.SHORT); // 0 - Slot
handler(wrapper -> wrapper.write(Type.FLAT_VAR_INT_ITEM, handleItemToServer(wrapper.read(Type.ITEM1_20_2)))); // 1 - Clicked Item
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.SPAWN_PARTICLE, new PacketHandlers() {
@Override
public void register() {
map(Type.VAR_INT); // 0 - Particle ID
map(Type.BOOLEAN); // 1 - Long Distance
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(wrapper -> {
final int id = wrapper.get(Type.VAR_INT, 0);
final ParticleMappings mappings = Protocol1_20_2To1_20.MAPPINGS.getParticleMappings();
if (mappings.isBlockParticle(id)) {
final int data = wrapper.read(Type.VAR_INT);
wrapper.write(Type.VAR_INT, protocol.getMappingData().getNewBlockStateId(data));
} else if (mappings.isItemParticle(id)) {
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM)));
}
});
}
});
new RecipeRewriter1_19_4<ClientboundPackets1_19_4>(protocol) {
@Override
public void handleCraftingShapeless(final PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
wrapper.passthrough(Type.VAR_INT); // Crafting book category
handleIngredients(wrapper);
final Item result = wrapper.read(itemType());
rewrite(result);
wrapper.write(Type.ITEM1_20_2, result);
}
@Override
public void handleSmelting(final PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
wrapper.passthrough(Type.VAR_INT); // Crafting book category
handleIngredient(wrapper);
final Item result = wrapper.read(itemType());
rewrite(result);
wrapper.write(Type.ITEM1_20_2, result);
wrapper.passthrough(Type.FLOAT); // EXP
wrapper.passthrough(Type.VAR_INT); // Cooking time
}
@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 i = 0; i < ingredients; i++) {
handleIngredient(wrapper);
}
final Item result = wrapper.read(itemType());
rewrite(result);
wrapper.write(Type.ITEM1_20_2, result);
wrapper.passthrough(Type.BOOLEAN); // Show notification
}
@Override
public void handleStonecutting(final PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
handleIngredient(wrapper);
final Item result = wrapper.read(itemType());
rewrite(result);
wrapper.write(Type.ITEM1_20_2, result);
}
@Override
public void handleSmithing(final PacketWrapper wrapper) throws Exception {
handleIngredient(wrapper); // Base
handleIngredient(wrapper); // Addition
final Item result = wrapper.read(itemType());
rewrite(result);
wrapper.write(Type.ITEM1_20_2, result);
}
@Override
public void handleSmithingTransform(final PacketWrapper wrapper) throws Exception {
handleIngredient(wrapper); // Template
handleIngredient(wrapper); // Base
handleIngredient(wrapper); // Additions
final Item result = wrapper.read(itemType());
rewrite(result);
wrapper.write(Type.ITEM1_20_2, result);
}
@Override
protected void handleIngredient(final PacketWrapper wrapper) throws Exception {
final Item[] items = wrapper.read(itemArrayType());
wrapper.write(Type.ITEM1_20_2_VAR_INT_ARRAY, items);
for (final Item item : items) {
rewrite(item);
}
}
}.register(ClientboundPackets1_19_4.DECLARE_RECIPES);
}
@Override
public @Nullable Item handleItemToClient(@Nullable final Item item) {
if (item == null) {
return null;
}
if (item.tag() != null) {
to1_20_2Effects(item);
}
return super.handleItemToClient(item);
}
@Override
public @Nullable Item handleItemToServer(@Nullable final Item item) {
if (item == null) {
return null;
}
if (item.tag() != null) {
to1_20_1Effects(item);
}
return super.handleItemToServer(item);
}
public static void to1_20_2Effects(final Item item) {
final Tag customPotionEffectsTag = item.tag().remove("CustomPotionEffects");
if (customPotionEffectsTag instanceof ListTag) {
final ListTag effectsTag = (ListTag) customPotionEffectsTag;
item.tag().put("custom_potion_effects", customPotionEffectsTag);
for (final Tag tag : effectsTag) {
if (!(tag instanceof CompoundTag)) {
continue;
}
final CompoundTag effectTag = (CompoundTag) tag;
final Tag idTag = effectTag.remove("Id");
if (idTag instanceof NumberTag) {
final String key = PotionEffects.idToKey(((NumberTag) idTag).asInt());
if (key != null) {
effectTag.put("id", new StringTag(key));
}
}
renameTag(effectTag, "Amplifier", "amplifier");
renameTag(effectTag, "Duration", "duration");
renameTag(effectTag, "Ambient", "ambient");
renameTag(effectTag, "ShowParticles", "show_particles");
renameTag(effectTag, "ShowIcon", "show_icon");
renameTag(effectTag, "HiddenEffect", "hidden_effect");
renameTag(effectTag, "FactorCalculationData", "factor_calculation_data");
}
}
}
public static void to1_20_1Effects(final Item item) {
final Tag customPotionEffectsTag = item.tag().remove("custom_potion_effects");
if (customPotionEffectsTag instanceof ListTag) {
final ListTag effectsTag = (ListTag) customPotionEffectsTag;
item.tag().put("CustomPotionEffects", effectsTag);
for (final Tag tag : effectsTag) {
if (!(tag instanceof CompoundTag)) {
continue;
}
final CompoundTag effectTag = (CompoundTag) tag;
final Tag idTag = effectTag.remove("id");
if (idTag instanceof StringTag) {
final int id = PotionEffects.keyToId(((StringTag) idTag).getValue());
effectTag.put("Id", new IntTag(id));
}
renameTag(effectTag, "amplifier", "Amplifier");
renameTag(effectTag, "duration", "Duration");
renameTag(effectTag, "ambient", "Ambient");
renameTag(effectTag, "show_particles", "ShowParticles");
renameTag(effectTag, "show_icon", "ShowIcon");
renameTag(effectTag, "hidden_effect", "HiddenEffect");
renameTag(effectTag, "factor_calculation_data", "FactorCalculationData");
}
}
}
private static void renameTag(final CompoundTag tag, final String entryName, final String toEntryName) {
final Tag entry = tag.remove(entryName);
if (entry != null) {
tag.put(toEntryName, entry);
}
}
private @Nullable CompoundTag handleBlockEntity(@Nullable final CompoundTag tag) {
if (tag == null) {
return null;
}
final IntTag primaryEffect = tag.remove("Primary");
if (primaryEffect != null && primaryEffect.asInt() != 0) {
tag.put("primary_effect", new StringTag(PotionEffects.idToKeyOrLuck(primaryEffect.asInt())));
}
final IntTag secondaryEffect = tag.remove("Secondary");
if (secondaryEffect != null && secondaryEffect.asInt() != 0) {
tag.put("secondary_effect", new StringTag(PotionEffects.idToKeyOrLuck(secondaryEffect.asInt())));
}
return tag;
}
}
@@ -0,0 +1,190 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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_20_2to1_20.rewriter;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_19_4Types;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.version.Types1_20;
import com.viaversion.viaversion.api.type.types.version.Types1_20_2;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.ClientboundPackets1_19_4;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.Protocol1_20_2To1_20;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ClientboundPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.storage.ConfigurationState;
import com.viaversion.viaversion.rewriter.EntityRewriter;
public final class EntityPacketRewriter1_20_2 extends EntityRewriter<ClientboundPackets1_19_4, Protocol1_20_2To1_20> {
public EntityPacketRewriter1_20_2(final Protocol1_20_2To1_20 protocol) {
super(protocol);
}
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_19_4.SPAWN_ENTITY, Entity1_19_4Types.FALLING_BLOCK);
registerMetadataRewriter(ClientboundPackets1_19_4.ENTITY_METADATA, Types1_20.METADATA_LIST, Types1_20_2.METADATA_LIST);
registerRemoveEntities(ClientboundPackets1_19_4.REMOVE_ENTITIES);
protocol.registerClientbound(ClientboundPackets1_19_4.SPAWN_PLAYER, ClientboundPackets1_20_2.SPAWN_ENTITY, wrapper -> {
wrapper.passthrough(Type.VAR_INT); // Entity id
wrapper.passthrough(Type.UUID); // UUID
wrapper.write(Type.VAR_INT, Entity1_19_4Types.PLAYER.getId()); // Entity type id
wrapper.passthrough(Type.DOUBLE); // X
wrapper.passthrough(Type.DOUBLE); // Y
wrapper.passthrough(Type.DOUBLE); // Z
final byte yaw = wrapper.read(Type.BYTE); // Yaw
wrapper.passthrough(Type.BYTE); // Pitch
wrapper.write(Type.BYTE, yaw);
wrapper.write(Type.BYTE, yaw); // Head yaw
wrapper.write(Type.VAR_INT, 0); // Data
wrapper.write(Type.SHORT, (short) 0); // Velocity X
wrapper.write(Type.SHORT, (short) 0); // Velocity Y
wrapper.write(Type.SHORT, (short) 0); // Velocity Z
});
protocol.registerClientbound(ClientboundPackets1_19_4.JOIN_GAME, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
// Just reorder written data, move dimension data to configuration phase
wrapper.passthrough(Type.INT); // Entity id
wrapper.passthrough(Type.BOOLEAN); // Hardcore
final byte gamemode = wrapper.read(Type.UNSIGNED_BYTE).byteValue();
final byte previousGamemode = wrapper.read(Type.BYTE);
wrapper.passthrough(Type.STRING_ARRAY); // World List
final CompoundTag dimensionRegistry = wrapper.read(Type.NBT);
final String dimensionType = wrapper.read(Type.STRING);
final String world = wrapper.read(Type.STRING);
final long seed = wrapper.read(Type.LONG);
trackBiomeSize(wrapper.user(), dimensionRegistry); // Caches dimensions to access data like height later
cacheDimensionData(wrapper.user(), dimensionRegistry); // Tracks the amount of biomes sent for chunk data
wrapper.passthrough(Type.VAR_INT); // Max players
wrapper.passthrough(Type.VAR_INT); // View distance
wrapper.passthrough(Type.VAR_INT); // Simulation distance
wrapper.passthrough(Type.BOOLEAN); // Reduced debug info
wrapper.passthrough(Type.BOOLEAN); // Show death screen
wrapper.write(Type.BOOLEAN, false); // Limited crafting
wrapper.write(Type.STRING, dimensionType);
wrapper.write(Type.STRING, world);
wrapper.write(Type.LONG, seed);
wrapper.write(Type.BYTE, gamemode);
wrapper.write(Type.BYTE, previousGamemode);
// Debug, flat, last death pos, and portal cooldown at the end unchanged
final ConfigurationState configurationBridge = wrapper.user().get(ConfigurationState.class);
if (!configurationBridge.setLastDimensionRegistry(dimensionRegistry)) {
// No change, so no need to re-enter the configuration state - just let this one through
final PacketWrapper clientInformationPacket = configurationBridge.clientInformationPacket(wrapper.user());
if (clientInformationPacket != null) {
clientInformationPacket.sendToServer(Protocol1_20_2To1_20.class);
}
return;
}
if (configurationBridge.bridgePhase() == ConfigurationState.BridgePhase.NONE) {
// Reenter the configuration state
final PacketWrapper configurationPacket = wrapper.create(ClientboundPackets1_20_2.START_CONFIGURATION);
configurationPacket.send(Protocol1_20_2To1_20.class);
configurationBridge.setBridgePhase(ConfigurationState.BridgePhase.REENTERING_CONFIGURATION);
configurationBridge.setJoinGamePacket(wrapper);
wrapper.cancel();
return;
}
Protocol1_20_2To1_20.sendConfigurationPackets(wrapper.user(), dimensionRegistry, null);
// Manually send it at the end and hope nothing breaks
configurationBridge.setJoinGamePacket(wrapper);
wrapper.cancel();
});
handler(worldDataTrackerHandlerByKey()); // Tracks world height and name for chunk data and entity (un)tracking
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.RESPAWN, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
wrapper.passthrough(Type.STRING); // Dimension type
wrapper.passthrough(Type.STRING); // World
wrapper.passthrough(Type.LONG); // Seed
wrapper.write(Type.BYTE, wrapper.read(Type.UNSIGNED_BYTE).byteValue()); // Gamemode
wrapper.passthrough(Type.BYTE); // Previous gamemode
wrapper.passthrough(Type.BOOLEAN); // Debug
wrapper.passthrough(Type.BOOLEAN); // Flat
// Move this to the end
final byte dataToKeep = wrapper.read(Type.BYTE);
wrapper.passthrough(Type.OPTIONAL_GLOBAL_POSITION); // Last death position
wrapper.passthrough(Type.VAR_INT); // Portal cooldown
wrapper.write(Type.BYTE, dataToKeep);
});
handler(worldDataTrackerHandlerByKey()); // Tracks world height and name for chunk data and entity (un)tracking
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.ENTITY_EFFECT, wrapper -> {
wrapper.passthrough(Type.VAR_INT); // Entity id
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) - 1); // Effect id
wrapper.passthrough(Type.BYTE); // Amplifier
wrapper.passthrough(Type.VAR_INT); // Duration
wrapper.passthrough(Type.BYTE); // Flags
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.write(Type.NAMELESS_NBT, wrapper.read(Type.NBT)); // Factor data
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.REMOVE_ENTITY_EFFECT, wrapper -> {
wrapper.passthrough(Type.VAR_INT); // Entity id
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) - 1); // Effect id
});
}
@Override
protected void registerRewrites() {
filter().handler((event, meta) -> meta.setMetaType(Types1_20_2.META_TYPES.byId(meta.metaType().typeId())));
registerMetaTypeHandler(null, Types1_20_2.META_TYPES.blockStateType, Types1_20_2.META_TYPES.optionalBlockStateType, Types1_20_2.META_TYPES.particleType);
filter().filterFamily(Entity1_19_4Types.DISPLAY).addIndex(10);
filter().filterFamily(Entity1_19_4Types.MINECART_ABSTRACT).index(11).handler((event, meta) -> {
final int blockState = meta.value();
meta.setValue(protocol.getMappingData().getNewBlockStateId(blockState));
});
}
@Override
public EntityType typeFromId(final int type) {
return Entity1_19_4Types.getTypeFromId(type);
}
}
@@ -0,0 +1,41 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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_20_2to1_20.rewriter;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.rewriter.RecipeRewriter1_19_4;
public class RecipeRewriter1_20_2<C extends ClientboundPacketType> extends RecipeRewriter1_19_4<C> {
public RecipeRewriter1_20_2(final Protocol<C, ?, ?, ?> protocol) {
super(protocol);
}
@Override
protected Type<Item> itemType() {
return Type.ITEM1_20_2;
}
@Override
protected Type<Item[]> itemArrayType() {
return Type.ITEM1_20_2_VAR_INT_ARRAY;
}
}
@@ -0,0 +1,247 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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_20_2to1_20.storage;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.ServerboundPackets1_19_4;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.Protocol1_20_2To1_20;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ConfigurationState implements StorableObject {
private final List<QueuedPacket> packetQueue = new ArrayList<>();
private BridgePhase bridgePhase = BridgePhase.NONE;
private QueuedPacket joinGamePacket;
private boolean queuedJoinGame;
private CompoundTag lastDimensionRegistry;
private ClientInformation clientInformation;
public BridgePhase bridgePhase() {
return bridgePhase;
}
public void setBridgePhase(final BridgePhase bridgePhase) {
this.bridgePhase = bridgePhase;
}
public @Nullable CompoundTag lastDimensionRegistry() {
return lastDimensionRegistry;
}
/**
* Sets the last dimension registry and returns whether it differs from the previously stored one.
*
* @param dimensionRegistry dimension registry to set
* @return whether the dimension registry differs from the previously stored one
*/
public boolean setLastDimensionRegistry(final CompoundTag dimensionRegistry) {
final boolean equals = Objects.equals(this.lastDimensionRegistry, dimensionRegistry);
this.lastDimensionRegistry = dimensionRegistry;
return !equals;
}
public void setClientInformation(final ClientInformation clientInformation) {
this.clientInformation = clientInformation;
}
public void addPacketToQueue(final PacketWrapper wrapper, final boolean clientbound) throws Exception {
packetQueue.add(toQueuedPacket(wrapper, clientbound, false));
}
private QueuedPacket toQueuedPacket(final PacketWrapper wrapper, final boolean clientbound, final boolean skipCurrentPipeline) throws Exception {
// Caching packet buffers is cursed, copy to heap buffers to make sure we don't start leaking in dumb cases
final ByteBuf copy = Unpooled.buffer();
final PacketType packetType = wrapper.getPacketType();
final int packetId = wrapper.getId();
// Don't write the packet id to the buffer
//noinspection deprecation
wrapper.setId(-1);
wrapper.writeToBuffer(copy);
return new QueuedPacket(copy, clientbound, packetType, packetId, skipCurrentPipeline);
}
public void setJoinGamePacket(final PacketWrapper wrapper) throws Exception {
this.joinGamePacket = toQueuedPacket(wrapper, true, true);
queuedJoinGame = true;
}
@Override
public boolean clearOnServerSwitch() {
return false; // This might be bad
}
@Override
public void onRemove() {
for (final QueuedPacket packet : packetQueue) {
packet.buf().release();
}
if (joinGamePacket != null) {
joinGamePacket.buf().release();
}
}
public void sendQueuedPackets(final UserConnection connection) throws Exception {
final boolean hasJoinGamePacket = joinGamePacket != null;
if (hasJoinGamePacket) {
packetQueue.add(0, joinGamePacket);
joinGamePacket = null;
}
final PacketWrapper clientInformationPacket = clientInformationPacket(connection);
if (clientInformationPacket != null) {
packetQueue.add(hasJoinGamePacket ? 1 : 0, toQueuedPacket(clientInformationPacket, false, true));
}
final ConfigurationState.QueuedPacket[] queuedPackets = packetQueue.toArray(new ConfigurationState.QueuedPacket[0]);
packetQueue.clear();
for (final ConfigurationState.QueuedPacket packet : queuedPackets) {
final PacketWrapper queuedWrapper;
try {
if (packet.packetType() != null) {
queuedWrapper = PacketWrapper.create(packet.packetType(), packet.buf(), connection);
} else {
//noinspection deprecation
queuedWrapper = PacketWrapper.create(packet.packetId(), packet.buf(), connection);
}
if (packet.clientbound()) {
queuedWrapper.send(Protocol1_20_2To1_20.class, packet.skipCurrentPipeline());
} else {
queuedWrapper.sendToServer(Protocol1_20_2To1_20.class, packet.skipCurrentPipeline());
}
} finally {
packet.buf().release();
}
}
}
public void clear() {
packetQueue.clear();
bridgePhase = BridgePhase.NONE;
queuedJoinGame = false;
}
public boolean queuedOrSentJoinGame() {
return queuedJoinGame;
}
public enum BridgePhase {
NONE, PROFILE_SENT, CONFIGURATION, REENTERING_CONFIGURATION
}
public @Nullable PacketWrapper clientInformationPacket(final UserConnection connection) {
if (clientInformation == null) {
// Should never be null, but we also shouldn't error
return null;
}
final PacketWrapper settingsPacket = PacketWrapper.create(ServerboundPackets1_19_4.CLIENT_SETTINGS, connection);
settingsPacket.write(Type.STRING, clientInformation.language);
settingsPacket.write(Type.BYTE, clientInformation.viewDistance);
settingsPacket.write(Type.VAR_INT, clientInformation.chatVisibility);
settingsPacket.write(Type.BOOLEAN, clientInformation.showChatColors);
settingsPacket.write(Type.UNSIGNED_BYTE, clientInformation.modelCustomization);
settingsPacket.write(Type.VAR_INT, clientInformation.mainHand);
settingsPacket.write(Type.BOOLEAN, clientInformation.textFiltering);
settingsPacket.write(Type.BOOLEAN, clientInformation.allowListing);
return settingsPacket;
}
public static final class QueuedPacket {
private final ByteBuf buf;
private final boolean clientbound;
private final PacketType packetType;
private final int packetId;
private final boolean skipCurrentPipeline;
private QueuedPacket(final ByteBuf buf, final boolean clientbound, final PacketType packetType,
final int packetId, final boolean skipCurrentPipeline) {
this.buf = buf;
this.clientbound = clientbound;
this.packetType = packetType;
this.packetId = packetId;
this.skipCurrentPipeline = skipCurrentPipeline;
}
public ByteBuf buf() {
return buf;
}
public boolean clientbound() {
return clientbound;
}
public int packetId() {
return packetId;
}
public @Nullable PacketType packetType() {
return packetType;
}
public boolean skipCurrentPipeline() {
return skipCurrentPipeline;
}
@Override
public String toString() {
return "QueuedPacket{" +
"buf=" + buf +
", clientbound=" + clientbound +
", packetType=" + packetType +
", packetId=" + packetId +
", skipCurrentPipeline=" + skipCurrentPipeline +
'}';
}
}
public static final class ClientInformation {
private final String language;
private final byte viewDistance;
private final int chatVisibility;
private final boolean showChatColors;
private final short modelCustomization;
private final int mainHand;
private final boolean textFiltering;
private final boolean allowListing;
public ClientInformation(final String language, final byte viewDistance, final int chatVisibility,
final boolean showChatColors, final short modelCustomization, final int mainHand,
final boolean textFiltering, final boolean allowListing) {
this.language = language;
this.viewDistance = viewDistance;
this.chatVisibility = chatVisibility;
this.showChatColors = showChatColors;
this.modelCustomization = modelCustomization;
this.mainHand = mainHand;
this.textFiltering = textFiltering;
this.allowListing = allowListing;
}
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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_20_2to1_20.storage;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.connection.StorableObject;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class LastResourcePack implements StorableObject {
private final String url;
private final String hash;
private final boolean required;
private final JsonElement prompt;
public LastResourcePack(final String url, final String hash, final boolean required, @Nullable final JsonElement prompt) {
this.url = url;
this.hash = hash;
this.required = required;
this.prompt = prompt;
}
public String url() {
return url;
}
public String hash() {
return hash;
}
public boolean required() {
return required;
}
public @Nullable JsonElement prompt() {
return prompt;
}
}
@@ -0,0 +1,100 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_20_2to1_20.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_18;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType;
import com.viaversion.viaversion.api.type.types.version.ChunkSectionType1_18;
import com.viaversion.viaversion.api.type.types.version.Types1_20_2;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
public final class ChunkType1_20_2 extends Type<Chunk> {
private final ChunkSectionType1_18 sectionType;
private final int ySectionCount;
public ChunkType1_20_2(final int ySectionCount, final int globalPaletteBlockBits, final int globalPaletteBiomeBits) {
super(Chunk.class);
Preconditions.checkArgument(ySectionCount > 0);
this.sectionType = new ChunkSectionType1_18(globalPaletteBlockBits, globalPaletteBiomeBits);
this.ySectionCount = ySectionCount;
}
@Override
public Chunk read(final ByteBuf buffer) throws Exception {
final int chunkX = buffer.readInt();
final int chunkZ = buffer.readInt();
final CompoundTag heightMap = Type.NAMELESS_NBT.read(buffer);
// Read sections
final ByteBuf sectionsBuf = buffer.readBytes(Type.VAR_INT.readPrimitive(buffer));
final ChunkSection[] sections = new ChunkSection[ySectionCount];
try {
for (int i = 0; i < ySectionCount; i++) {
sections[i] = sectionType.read(sectionsBuf);
}
} finally {
sectionsBuf.release();
}
final int blockEntitiesLength = Type.VAR_INT.readPrimitive(buffer);
final List<BlockEntity> blockEntities = new ArrayList<>(blockEntitiesLength);
for (int i = 0; i < blockEntitiesLength; i++) {
blockEntities.add(Types1_20_2.BLOCK_ENTITY.read(buffer));
}
return new Chunk1_18(chunkX, chunkZ, sections, heightMap, blockEntities);
}
@Override
public void write(final ByteBuf buffer, final Chunk chunk) throws Exception {
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
Type.NAMELESS_NBT.write(buffer, chunk.getHeightMap());
final ByteBuf sectionBuffer = buffer.alloc().buffer();
try {
for (final ChunkSection section : chunk.getSections()) {
sectionType.write(sectionBuffer, section);
}
sectionBuffer.readerIndex(0);
Type.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());
buffer.writeBytes(sectionBuffer);
} finally {
sectionBuffer.release(); // release buffer
}
Type.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());
for (final BlockEntity blockEntity : chunk.blockEntities()) {
Types1_20_2.BLOCK_ENTITY.write(buffer, blockEntity);
}
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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_20_2to1_20.util;
import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class PotionEffects {
private static final Object2IntMap<String> KEY_TO_ID = new Object2IntOpenHashMap<>();
private static final String[] POTION_EFFECTS = {
"", // No effect
"speed",
"slowness",
"haste",
"mining_fatigue",
"strength",
"instant_health",
"instant_damage",
"jump_boost",
"nausea",
"regeneration",
"resistance",
"fire_resistance",
"water_breathing",
"invisibility",
"blindness",
"night_vision",
"hunger",
"weakness",
"poison",
"wither",
"health_boost",
"absorption",
"saturation",
"glowing",
"levitation",
"luck",
"unluck",
"slow_falling",
"conduit_power",
"dolphins_grace",
"bad_omen",
"hero_of_the_village",
"darkness"
};
static {
for (int i = 1; i < POTION_EFFECTS.length; i++) {
final String effect = POTION_EFFECTS[i];
KEY_TO_ID.put(effect, i);
}
}
public static @Nullable String idToKey(final int id) {
return id >= 1 && id < POTION_EFFECTS.length ? Key.namespaced(POTION_EFFECTS[id]) : null;
}
public static String idToKeyOrLuck(final int id) {
return id >= 1 && id < POTION_EFFECTS.length ? Key.namespaced(POTION_EFFECTS[id]) : "minecraft:luck";
}
public static int keyToId(final String key) {
return KEY_TO_ID.getInt(Key.stripMinecraftNamespace(key));
}
}
@@ -23,8 +23,10 @@ import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.api.minecraft.Quaternion;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_19_4Types;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.version.Types1_19_4;
@@ -35,6 +37,8 @@ import com.viaversion.viaversion.rewriter.EntityRewriter;
public final class EntityPackets extends EntityRewriter<ClientboundPackets1_19_4, Protocol1_20To1_19_4> {
private static final Quaternion Y_FLIPPED_ROTATION = new Quaternion(0, 1, 0, 0);
public EntityPackets(final Protocol1_20To1_19_4 protocol) {
super(protocol);
}
@@ -127,6 +131,21 @@ public final class EntityPackets extends EntityRewriter<ClientboundPackets1_19_4
filter().handler((event, meta) -> meta.setMetaType(Types1_20.META_TYPES.byId(meta.metaType().typeId())));
registerMetaTypeHandler(Types1_20.META_TYPES.itemType, Types1_20.META_TYPES.blockStateType, Types1_20.META_TYPES.optionalBlockStateType, Types1_20.META_TYPES.particleType);
// Rotate item display by 180 degrees around the Y axis
filter().filterFamily(Entity1_19_4Types.ITEM_DISPLAY).handler((event, meta) -> {
if (event.trackedEntity().hasSentMetadata() || event.hasExtraMeta()) {
return;
}
if (event.metaAtIndex(12) == null) {
event.createExtraMeta(new Metadata(12, Types1_20.META_TYPES.quaternionType, Y_FLIPPED_ROTATION));
}
});
filter().filterFamily(Entity1_19_4Types.ITEM_DISPLAY).index(12).handler((event, meta) -> {
final Quaternion quaternion = meta.value();
meta.setValue(rotateY180(quaternion));
});
filter().filterFamily(Entity1_19_4Types.MINECART_ABSTRACT).index(11).handler((event, meta) -> {
final int blockState = meta.value();
meta.setValue(protocol.getMappingData().getNewBlockStateId(blockState));
@@ -137,4 +156,8 @@ public final class EntityPackets extends EntityRewriter<ClientboundPackets1_19_4
public EntityType typeFromId(final int type) {
return Entity1_19_4Types.getTypeFromId(type);
}
private Quaternion rotateY180(final Quaternion quaternion) {
return new Quaternion(-quaternion.z(), quaternion.w(), quaternion.x(), -quaternion.y());
}
}
@@ -67,9 +67,9 @@ public final class InventoryPackets extends ItemRewriter<ClientboundPackets1_19_
for (int i = 0; i < size; i++) {
wrapper.passthrough(Type.STRING); // Identifier
// Parent
if (wrapper.passthrough(Type.BOOLEAN))
wrapper.passthrough(Type.STRING);
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.passthrough(Type.STRING); // Parent
}
// Display data
if (wrapper.passthrough(Type.BOOLEAN)) {
@@ -85,11 +85,11 @@ public final class InventoryPackets extends ItemRewriter<ClientboundPackets1_19_
wrapper.passthrough(Type.FLOAT); // Y
}
wrapper.passthrough(Type.STRING_ARRAY); // Criteria
wrapper.passthrough(Type.STRING_ARRAY); // Critereon triggers
int arrayLength = wrapper.passthrough(Type.VAR_INT);
for (int array = 0; array < arrayLength; array++) {
wrapper.passthrough(Type.STRING_ARRAY); // String array
int requirements = wrapper.passthrough(Type.VAR_INT);
for (int array = 0; array < requirements; array++) {
wrapper.passthrough(Type.STRING_ARRAY);
}
wrapper.write(Type.BOOLEAN, false); // Sends telemetry
@@ -172,17 +172,19 @@ public final class InventoryPackets extends ItemRewriter<ClientboundPackets1_19_
final ListTag messages = new ListTag(StringTag.class);
for (int i = 1; i < 5; i++) {
final Tag text = tag.get("Text" + i);
final Tag text = tag.remove("Text" + i);
messages.add(text != null ? text : new StringTag(ChatRewriter.emptyComponentString()));
}
frontText.put("messages", messages);
final ListTag filteredMessages = new ListTag(StringTag.class);
for (int i = 1; i < 5; i++) {
final Tag text = tag.get("FilteredText" + i);
filteredMessages.add(text != null ? text : new StringTag(ChatRewriter.emptyComponentString()));
final Tag text = tag.remove("FilteredText" + i);
filteredMessages.add(text != null ? text : messages.get(i - 1));
}
if (!filteredMessages.equals(messages)) {
frontText.put("filtered_messages", filteredMessages);
}
frontText.put("filtered_messages", filteredMessages);
final Tag color = tag.remove("Color");
if (color != null) {
@@ -1,121 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_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;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_9_1_2to1_9_3_4.chunks.BlockEntity;
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;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_1_2Type;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ClientboundPackets1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ServerboundPackets1_9;
// Goes BACKWARDS from 1.9.3/4 to 1.9.1/2
public class Protocol1_9_1_2To1_9_3_4 extends AbstractProtocol<ClientboundPackets1_9_3, ClientboundPackets1_9, ServerboundPackets1_9_3, ServerboundPackets1_9> {
public Protocol1_9_1_2To1_9_3_4() {
super(ClientboundPackets1_9_3.class, ClientboundPackets1_9.class, ServerboundPackets1_9_3.class, ServerboundPackets1_9.class);
}
@Override
protected void registerPackets() {
registerClientbound(ClientboundPackets1_9_3.BLOCK_ENTITY_DATA, new PacketHandlers() {
@Override
public void register() {
map(Type.POSITION); //Position
map(Type.UNSIGNED_BYTE); //Type
map(Type.NBT); //NBT
handler(wrapper -> {
if (wrapper.get(Type.UNSIGNED_BYTE, 0) == 9) {
Position position = wrapper.get(Type.POSITION, 0);
CompoundTag tag = wrapper.get(Type.NBT, 0);
wrapper.clearPacket(); //Clear the packet
wrapper.setPacketType(ClientboundPackets1_9.UPDATE_SIGN);
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
Tag textTag = tag.get("Text" + i);
String line = textTag instanceof StringTag ? ((StringTag) textTag).getValue() : "";
wrapper.write(Type.STRING, line); // Sign line
}
}
});
}
});
registerClientbound(ClientboundPackets1_9_3.CHUNK_DATA, wrapper -> {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
Chunk1_9_3_4Type newType = new Chunk1_9_3_4Type(clientWorld);
Chunk1_9_1_2Type oldType = new Chunk1_9_1_2Type(clientWorld); // Get the old type to not write Block Entities
Chunk chunk = wrapper.read(newType);
wrapper.write(oldType, chunk);
BlockEntity.handle(chunk.getBlockEntities(), wrapper.user());
});
registerClientbound(ClientboundPackets1_9_3.JOIN_GAME, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // 0 - Entity ID
map(Type.UNSIGNED_BYTE); // 1 - Gamemode
map(Type.INT); // 2 - Dimension
handler(wrapper -> {
ClientWorld clientChunks = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 1);
clientChunks.setEnvironment(dimensionId);
});
}
});
registerClientbound(ClientboundPackets1_9_3.RESPAWN, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // 0 - Dimension ID
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 0);
clientWorld.setEnvironment(dimensionId);
});
}
});
}
@Override
public void init(UserConnection userConnection) {
if (!userConnection.has(ClientWorld.class)) {
userConnection.put(new ClientWorld(userConnection));
}
}
}
@@ -1,84 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_9_1_2to1_9_3_4.chunks;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
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_3to1_9_1_2.ClientboundPackets1_9_3;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BlockEntity {
private static final Map<String, Integer> types = new HashMap<>();
static {
types.put("MobSpawner", 1);
types.put("Control", 2);
types.put("Beacon", 3);
types.put("Skull", 4);
types.put("FlowerPot", 5);
types.put("Banner", 6);
types.put("UNKNOWN", 7);
types.put("EndGateway", 8);
types.put("Sign", 9);
}
public static void handle(List<CompoundTag> tags, UserConnection connection) {
for (CompoundTag tag : tags) {
try {
if (!tag.contains("id"))
throw new Exception("NBT tag not handled because the id key is missing");
String id = (String) tag.get("id").getValue();
if (!types.containsKey(id))
throw new Exception("Not handled id: " + id);
int newId = types.get(id);
if (newId == -1)
continue;
int x = ((NumberTag) tag.get("x")).asInt();
int y = ((NumberTag) tag.get("y")).asInt();
int z = ((NumberTag) tag.get("z")).asInt();
Position pos = new Position(x, (short) y, z);
updateBlockEntity(pos, (short) newId, tag, connection);
} catch (Exception e) {
if (Via.getManager().isDebug()) {
Via.getPlatform().getLogger().warning("Block Entity: " + e.getMessage() + ": " + tag);
}
}
}
}
private static void updateBlockEntity(Position pos, short id, CompoundTag tag, UserConnection connection) throws Exception {
PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_9_3.BLOCK_ENTITY_DATA, null, connection);
wrapper.write(Type.POSITION, pos);
wrapper.write(Type.UNSIGNED_BYTE, id);
wrapper.write(Type.NBT, tag);
wrapper.scheduleSend(Protocol1_9_1_2To1_9_3_4.class, false);
}
}
@@ -15,7 +15,7 @@
* 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_9_3to1_9_1_2.types;
package com.viaversion.viaversion.protocols.protocol1_9_1to1_9.types;
import com.viaversion.viaversion.api.minecraft.Environment;
import com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;
@@ -32,10 +32,10 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;
import com.viaversion.viaversion.api.type.Type;
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.types.Chunk1_9_3_4Type;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.chunks.FakeTileEntity;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_1_2Type;
import com.viaversion.viaversion.protocols.protocol1_9_1to1_9.types.Chunk1_9_1_2Type;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ClientboundPackets1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ServerboundPackets1_9;
import java.util.List;
@@ -15,7 +15,7 @@
* 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_9_1_2to1_9_3_4.types;
package com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.Via;
@@ -122,4 +122,4 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
}
}
@@ -37,13 +37,7 @@ import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.InventoryPac
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.PlayerPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.SpawnPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.WorldPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.BossBarProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.CommandBlockProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.CompressionProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.EntityIdProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MainHandProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.*;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.ClientChunks;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.CommandBlockStorage;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.EntityTracker1_9;
@@ -137,7 +131,7 @@ public class Protocol1_9To1_8 extends AbstractProtocol<ClientboundPackets1_8, Cl
providers.register(BossBarProvider.class, new BossBarProvider());
providers.register(MainHandProvider.class, new MainHandProvider());
providers.register(CompressionProvider.class, new CompressionProvider());
providers.require(MovementTransmitterProvider.class);
providers.register(MovementTransmitterProvider.class, new MovementTransmitterProvider());
}
@Override
@@ -33,7 +33,7 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_8.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_8.ServerboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import com.viaversion.viaversion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_1_2Type;
import com.viaversion.viaversion.protocols.protocol1_9_1to1_9.types.Chunk1_9_1_2Type;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ClientboundPackets1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ItemRewriter;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
@@ -17,27 +17,34 @@
*/
package com.viaversion.viaversion.protocols.protocol1_9to1_8.providers;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.providers.Provider;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_8.ServerboundPackets1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.MovementTracker;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.channel.ChannelHandlerContext;
public abstract class MovementTransmitterProvider implements Provider {
public abstract Object getFlyingPacket();
import java.util.logging.Level;
public abstract Object getGroundPacket();
public class MovementTransmitterProvider implements Provider {
public void sendPlayer(UserConnection userConnection) {
// Old method using packets.
ChannelHandlerContext context = PipelineUtil.getContextBefore("decoder", userConnection.getChannel().pipeline());
if (context != null) {
if (userConnection.get(MovementTracker.class).isGround()) {
context.fireChannelRead(getGroundPacket());
} else {
context.fireChannelRead(getFlyingPacket());
}
userConnection.get(MovementTracker.class).incrementIdlePacket();
if (userConnection.getProtocolInfo().getClientState() != State.PLAY || userConnection.getEntityTracker(Protocol1_9To1_8.class).clientEntityId() == -1) {
return;
}
final MovementTracker movementTracker = userConnection.get(MovementTracker.class);
movementTracker.incrementIdlePacket();
try {
final PacketWrapper playerMovement = PacketWrapper.create(ServerboundPackets1_8.PLAYER_MOVEMENT, userConnection);
playerMovement.write(Type.BOOLEAN, movementTracker.isGround()); // on ground
playerMovement.scheduleSendToServer(Protocol1_9To1_8.class);
} catch (Throwable e) {
Via.getPlatform().getLogger().log(Level.WARNING, "Failed to send player movement packet", e);
}
}
}
@@ -42,8 +42,8 @@ public class Effect {
addRewrite(1020, 1029); //Anvil break
addRewrite(1021, 1030); //Anvil use
addRewrite(1022, 1031); //Anvil land
addRewrite(1013, 1023);
addRewrite(1018, 1028);
addRewrite(1013, 1023); //Wither spawn
addRewrite(1018, 1028); //EnderDragon end
}
public static int getNewId(int id) {
@@ -23,7 +23,7 @@ public class MovementTracker implements StorableObject {
private static final long IDLE_PACKET_DELAY = 50L; // Update every 50ms (20tps)
private static final long IDLE_PACKET_LIMIT = 20; // Max 20 ticks behind
private long nextIdlePacket = 0L;
private boolean ground = true;
private boolean ground = false;
public void incrementIdlePacket() {
// Notify of next update
@@ -1,64 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2023 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_9to1_9_1;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ClientboundPackets1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ServerboundPackets1_9;
public class Protocol1_9To1_9_1 extends AbstractProtocol<ClientboundPackets1_9, ClientboundPackets1_9, ServerboundPackets1_9, ServerboundPackets1_9> {
public Protocol1_9To1_9_1() {
super(ClientboundPackets1_9.class, ClientboundPackets1_9.class, ServerboundPackets1_9.class, ServerboundPackets1_9.class);
}
@Override
protected void registerPackets() {
registerClientbound(ClientboundPackets1_9.JOIN_GAME, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // 0 - Player ID
map(Type.UNSIGNED_BYTE); // 1 - Player Gamemode
// 1.9.1 PRE 2 Changed this
map(Type.INT, Type.BYTE); // 2 - Player Dimension
map(Type.UNSIGNED_BYTE); // 3 - World Difficulty
map(Type.UNSIGNED_BYTE); // 4 - Max Players (Tab)
map(Type.STRING); // 5 - Level Type
map(Type.BOOLEAN); // 6 - Reduced Debug info
}
});
registerClientbound(ClientboundPackets1_9.SOUND, new PacketHandlers() {
@Override
public void register() {
map(Type.VAR_INT); // 0 - Sound ID
handler(wrapper -> {
int sound = wrapper.get(Type.VAR_INT, 0);
if (sound == 415) // Stop the Elytra sound for 1.9 (It's introduced in 1.9.2)
wrapper.cancel();
else if (sound >= 416) // Act like the Elytra sound never existed
wrapper.set(Type.VAR_INT, 0, sound - 1);
});
}
});
}
}
@@ -42,10 +42,16 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public class BlockRewriter<C extends ClientboundPacketType> {
private final Protocol<C, ?, ?, ?> protocol;
private final Type<Position> positionType;
private final Type<CompoundTag> nbtType;
public BlockRewriter(Protocol<C, ?, ?, ?> protocol, Type<Position> positionType) {
this(protocol, positionType, Type.NBT);
}
public BlockRewriter(Protocol<C, ?, ?, ?> protocol, Type<Position> positionType, Type<CompoundTag> nbtType) {
this.protocol = protocol;
this.positionType = positionType;
this.nbtType = nbtType;
}
public void registerBlockAction(C packetType) {
@@ -145,9 +151,9 @@ public class BlockRewriter<C extends ClientboundPacketType> {
handler(wrapper -> {
int id = wrapper.get(Type.INT, 0);
int data = wrapper.get(Type.INT, 1);
if (id == playRecordId) { // Play record
if (id == playRecordId && protocol.getMappingData().getItemMappings() != null) {
wrapper.set(Type.INT, 1, protocol.getMappingData().getNewItemId(data));
} else if (id == blockBreakId) { // Block break + block break sound
} else if (id == blockBreakId && protocol.getMappingData().getBlockStateMappings() != null) {
wrapper.set(Type.INT, 1, protocol.getMappingData().getNewBlockStateId(data));
}
});
@@ -166,8 +172,8 @@ public class BlockRewriter<C extends ClientboundPacketType> {
public PacketHandler chunkDataHandler1_19(ChunkTypeSupplier chunkTypeSupplier, @Nullable Consumer<BlockEntity> blockEntityHandler) {
return 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");
Preconditions.checkArgument(tracker.biomesSent() != -1, "Biome count not set");
Preconditions.checkArgument(tracker.currentWorldSectionHeight() != -1, "Section height not set");
final Type<Chunk> chunkType = chunkTypeSupplier.supply(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),
MathUtil.ceilLog2(tracker.biomesSent()));
@@ -203,7 +209,7 @@ public class BlockRewriter<C extends ClientboundPacketType> {
public void registerBlockEntityData(C packetType, @Nullable Consumer<BlockEntity> blockEntityHandler) {
protocol.registerClientbound(packetType, wrapper -> {
final Position position = wrapper.passthrough(Type.POSITION1_14);
final Position position = wrapper.passthrough(positionType);
final int blockEntityId = wrapper.read(Type.VAR_INT);
final Mappings mappings = protocol.getMappingData().getBlockEntityMappings();
@@ -214,7 +220,7 @@ public class BlockRewriter<C extends ClientboundPacketType> {
}
final CompoundTag tag;
if (blockEntityHandler != null && (tag = wrapper.passthrough(Type.NBT)) != null) {
if (blockEntityHandler != null && (tag = wrapper.passthrough(nbtType)) != null) {
final BlockEntity blockEntity = new BlockEntityImpl(BlockEntity.pack(position.x(), position.z()), (short) position.y(), blockEntityId, tag);
blockEntityHandler.accept(blockEntity);
}
@@ -101,47 +101,46 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
}
@Override
public void handleMetadata(int entityId, List<Metadata> metadataList, UserConnection connection) {
public void handleMetadata(final int entityId, final List<Metadata> metadataList, final UserConnection connection) {
final TrackedEntity entity = tracker(connection).entity(entityId);
final EntityType type = entity != null ? entity.entityType() : null;
int i = 0; // Count index for fast removal
for (Metadata metadata : metadataList.toArray(EMPTY_ARRAY)) { // Copy the list to allow mutation
for (final Metadata metadata : metadataList.toArray(EMPTY_ARRAY)) { // Copy the list to allow mutation
// Call handlers implementing the old handleMetadata
if (!callOldMetaHandler(entityId, type, metadata, metadataList, connection)) {
metadataList.remove(i--);
metadataList.remove(metadata);
continue;
}
MetaHandlerEvent event = null;
for (MetaFilter filter : metadataFilters) {
for (final MetaFilter filter : metadataFilters) {
if (!filter.isFiltered(type, metadata)) {
continue;
}
if (event == null) {
// Only initialize when needed and share event instance
event = new MetaHandlerEventImpl(connection, type, entityId, metadata, metadataList);
event = new MetaHandlerEventImpl(connection, entity, entityId, metadata, metadataList);
}
try {
filter.handler().handle(event, metadata);
} catch (Exception e) {
} catch (final Exception e) {
logException(e, type, metadataList, metadata);
metadataList.remove(i--);
metadataList.remove(metadata);
break;
}
if (event.cancelled()) {
// Remove meta, decrease list index counter, and break current filter loop
metadataList.remove(i--);
// Remove meta, and break current filter loop
metadataList.remove(metadata);
break;
}
}
if (event != null && event.extraMeta() != null) {
final List<Metadata> extraMeta = event != null ? event.extraMeta() : null;
if (extraMeta != null) {
// Finally, add newly created meta
metadataList.addAll(event.extraMeta());
metadataList.addAll(extraMeta);
}
i++;
}
if (entity != null) {
@@ -311,6 +310,10 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
map(Type.VAR_INT); // Data
handler(trackerHandler());
handler(wrapper -> {
if (protocol.getMappingData() == null) {
return;
}
int entityId = wrapper.get(Type.VAR_INT, 0);
EntityType entityType = tracker(wrapper.user()).entityType(entityId);
if (entityType == fallingBlockType) {
@@ -468,32 +471,32 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
}
public PacketHandler biomeSizeTracker() {
return wrapper -> {
final CompoundTag registry = wrapper.get(Type.NBT, 0);
final CompoundTag biomeRegistry = registry.get("minecraft:worldgen/biome");
final ListTag biomes = biomeRegistry.get("value");
tracker(wrapper.user()).setBiomesSent(biomes.size());
};
return wrapper -> trackBiomeSize(wrapper.user(), wrapper.get(Type.NBT, 0));
}
public void trackBiomeSize(final UserConnection connection, final CompoundTag registry) {
final CompoundTag biomeRegistry = registry.get("minecraft:worldgen/biome");
final ListTag biomes = biomeRegistry.get("value");
tracker(connection).setBiomesSent(biomes.size());
}
public PacketHandler dimensionDataHandler() {
return wrapper -> cacheDimensionData(wrapper.user(), wrapper.get(Type.NBT, 0));
}
/**
* Returns a handler to cache dimension data, later used to get height values and other important info.
*
* @return handler to cache dimension data
* Caches dimension data, later used to get height values and other important info.
*/
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);
};
public void cacheDimensionData(final UserConnection connection, final CompoundTag registry) {
final ListTag dimensions = ((CompoundTag) registry.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(connection).setDimensions(dimensionDataMap);
}
// ---------------------------------------------------------------------------
@@ -31,9 +31,17 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public abstract class ItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,
T extends Protocol<C, ?, ?, S>> extends RewriterBase<T> implements com.viaversion.viaversion.api.rewriter.ItemRewriter<T> {
private final Type<Item> itemType;
private final Type<Item[]> itemArrayType;
protected ItemRewriter(T protocol) {
this(protocol, Type.FLAT_VAR_INT_ITEM, Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT);
}
public ItemRewriter(T protocol, Type<Item> itemType, Type<Item[]> itemArrayType) {
super(protocol);
this.itemType = itemType;
this.itemArrayType = itemArrayType;
}
// These two methods always return the same item instance *for now*
@@ -62,7 +70,7 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
public void register() {
map(Type.UNSIGNED_BYTE); // Window id
map(type); // Items
handler(itemArrayHandler(type));
handler(itemArrayToClientHandler(type));
}
});
}
@@ -74,12 +82,12 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
handler(wrapper -> {
Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT);
Item[] items = wrapper.passthrough(itemArrayType);
for (Item item : items) {
handleItemToClient(item);
}
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Carried item
handleItemToClient(wrapper.passthrough(itemType)); // Carried item
});
}
});
@@ -123,8 +131,8 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
map(Type.SHORT); // Slot id
map(Type.FLAT_VAR_INT_ITEM); // Item
handler(itemToClientHandler(Type.FLAT_VAR_INT_ITEM));
map(itemType); // Item
handler(itemToClientHandler(itemType));
}
});
}
@@ -155,7 +163,7 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
do {
slot = wrapper.passthrough(Type.BYTE);
// & 0x7F into an extra variable if slot is needed
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM));
handleItemToClient(wrapper.passthrough(itemType));
} while ((slot & 0xFFFFFF80) != 0);
});
}
@@ -168,7 +176,6 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
public void register() {
map(Type.SHORT); // 0 - Slot
map(type); // 1 - Clicked Item
handler(itemToServerHandler(type));
}
});
@@ -205,11 +212,11 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
int length = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < length; i++) {
wrapper.passthrough(Type.SHORT); // Slot
handleItemToServer(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM));
handleItemToServer(wrapper.passthrough(itemType));
}
// Carried item
handleItemToServer(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM));
handleItemToServer(wrapper.passthrough(itemType));
});
}
});
@@ -228,11 +235,11 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
wrapper.passthrough(Type.VAR_INT);
int size = wrapper.passthrough(Type.UNSIGNED_BYTE);
for (int i = 0; i < size; i++) {
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Input
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Output
handleItemToClient(wrapper.passthrough(itemType)); // Input
handleItemToClient(wrapper.passthrough(itemType)); // Output
if (wrapper.passthrough(Type.BOOLEAN)) { // Has second item
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Second Item
handleItemToClient(wrapper.passthrough(itemType)); // Second Item
}
wrapper.passthrough(Type.BOOLEAN); // Trade disabled
@@ -253,9 +260,9 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
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.FLAT_VAR_INT_ITEM)); // Input
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Output
handleItemToClient(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Second Item
handleItemToClient(wrapper.passthrough(itemType)); // Input
handleItemToClient(wrapper.passthrough(itemType)); // Output
handleItemToClient(wrapper.passthrough(itemType)); // Second Item
wrapper.passthrough(Type.BOOLEAN); // Trade disabled
wrapper.passthrough(Type.INT); // Number of tools uses
@@ -304,6 +311,41 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
});
}
public void registerAdvancements1_20_2(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
wrapper.passthrough(Type.BOOLEAN); // Reset/clear
int size = wrapper.passthrough(Type.VAR_INT); // Mapping size
for (int i = 0; i < size; i++) {
wrapper.passthrough(Type.STRING); // Identifier
// Parent
if (wrapper.passthrough(Type.BOOLEAN))
wrapper.passthrough(Type.STRING);
// Display data
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.passthrough(Type.COMPONENT); // Title
wrapper.passthrough(Type.COMPONENT); // Description
handleItemToClient(wrapper.passthrough(itemType)); // Icon
wrapper.passthrough(Type.VAR_INT); // Frame type
int flags = wrapper.passthrough(Type.INT); // Flags
if ((flags & 1) != 0) {
wrapper.passthrough(Type.STRING); // Background texture
}
wrapper.passthrough(Type.FLOAT); // X
wrapper.passthrough(Type.FLOAT); // Y
}
int requirements = wrapper.passthrough(Type.VAR_INT);
for (int array = 0; array < requirements; array++) {
wrapper.passthrough(Type.STRING_ARRAY);
}
wrapper.passthrough(Type.BOOLEAN); // Send telemetry
}
});
}
public void registerWindowPropertyEnchantmentHandler(C packetType) {
protocol.registerClientbound(packetType, new PacketHandlers() {
@Override
@@ -340,7 +382,7 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
map(Type.FLOAT); // 7 - Offset Z
map(Type.FLOAT); // 8 - Particle Data
map(Type.INT); // 9 - Particle Count
handler(getSpawnParticleHandler(itemType));
handler(getSpawnParticleHandler());
}
});
}
@@ -359,16 +401,16 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
map(Type.FLOAT); // 7 - Offset Z
map(Type.FLOAT); // 8 - Particle Data
map(Type.INT); // 9 - Particle Count
handler(getSpawnParticleHandler(Type.VAR_INT, Type.FLAT_VAR_INT_ITEM));
handler(getSpawnParticleHandler(Type.VAR_INT));
}
});
}
public PacketHandler getSpawnParticleHandler(Type<Item> itemType) {
return getSpawnParticleHandler(Type.INT, itemType);
public PacketHandler getSpawnParticleHandler() {
return getSpawnParticleHandler(Type.INT);
}
public PacketHandler getSpawnParticleHandler(Type<Integer> idType, Type<Item> itemType) {
public PacketHandler getSpawnParticleHandler(Type<Integer> idType) {
return wrapper -> {
int id = wrapper.get(idType, 0);
if (id == -1) {
@@ -390,8 +432,7 @@ public abstract class ItemRewriter<C extends ClientboundPacketType, S extends Se
};
}
// Only sent to the client
public PacketHandler itemArrayHandler(Type<Item[]> type) {
public PacketHandler itemArrayToClientHandler(Type<Item[]> type) {
return wrapper -> {
Item[] items = wrapper.get(type, 0);
for (Item item : items) {
@@ -82,19 +82,19 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
for (int i = 0; i < ingredientsNo; i++) {
handleIngredient(wrapper);
}
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
}
public void handleCraftingShapeless(PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
handleIngredients(wrapper);
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
}
public void handleSmelting(PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
handleIngredient(wrapper);
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
wrapper.passthrough(Type.FLOAT); // EXP
wrapper.passthrough(Type.VAR_INT); // Cooking time
}
@@ -102,13 +102,13 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
public void handleStonecutting(PacketWrapper wrapper) throws Exception {
wrapper.passthrough(Type.STRING); // Group
handleIngredient(wrapper);
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
}
public void handleSmithing(PacketWrapper wrapper) throws Exception {
handleIngredient(wrapper); // Base
handleIngredient(wrapper); // Addition
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
}
public void handleSimpleRecipe(final PacketWrapper wrapper) throws Exception {
@@ -119,7 +119,7 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
handleIngredient(wrapper); // Template
handleIngredient(wrapper); // Base
handleIngredient(wrapper); // Additions
rewrite(wrapper.passthrough(Type.FLAT_VAR_INT_ITEM)); // Result
rewrite(wrapper.passthrough(itemType())); // Result
}
public void handleSmithingTrim(final PacketWrapper wrapper) throws Exception {
@@ -135,7 +135,7 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
}
protected void handleIngredient(final PacketWrapper wrapper) throws Exception {
final Item[] items = wrapper.passthrough(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT);
final Item[] items = wrapper.passthrough(itemArrayType());
for (final Item item : items) {
rewrite(item);
}
@@ -153,4 +153,12 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
void accept(PacketWrapper wrapper) throws Exception;
}
protected Type<Item> itemType() {
return Type.FLAT_VAR_INT_ITEM;
}
protected Type<Item[]> itemArrayType() {
return Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT;
}
}
@@ -37,7 +37,6 @@ public class SoundRewriter<C extends ClientboundPacketType> {
this.idRewriter = idRewriter;
}
// The same for entity sound
public void registerSound(C packetType) {
protocol.registerClientbound(packetType, new PacketHandlers() {
@Override
@@ -48,6 +47,10 @@ public class SoundRewriter<C extends ClientboundPacketType> {
});
}
public void registerEntitySound(C packetType) {
this.registerSound(packetType);
}
// Not for entity sounds
public void register1_19_3Sound(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
@@ -18,11 +18,13 @@
package com.viaversion.viaversion.rewriter.meta;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
public interface MetaHandlerEvent {
/**
@@ -39,12 +41,21 @@ public interface MetaHandlerEvent {
*/
int entityId();
/**
* Returns the tracked entity if present.
*
* @return tracked entity if present, else null
*/
@Nullable TrackedEntity trackedEntity();
/**
* Returns the entity type of the entity the metadata belongs to if tracked.
*
* @return entity type of the entity if tracked, else null
*/
@Nullable EntityType entityType();
default @Nullable EntityType entityType() {
return trackedEntity() != null ? trackedEntity().entityType() : null;
}
/**
* Returns the metadata index.
@@ -110,6 +121,15 @@ public interface MetaHandlerEvent {
*/
@Nullable List<Metadata> extraMeta();
/**
* Returns whether additionally created metadata will be added.
*
* @return true if additionally created metadata is present
*/
default boolean hasExtraMeta() {
return extraMeta() != null;
}
/**
* Adds the given metadata to the metadata list.
* This metadata will not be passed through handlers of the current loop.
@@ -19,25 +19,26 @@
package com.viaversion.viaversion.rewriter.meta;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
public class MetaHandlerEventImpl implements MetaHandlerEvent {
private final UserConnection connection;
private final EntityType entityType;
private final TrackedEntity trackedEntity;
private final int entityId;
private final List<Metadata> metadataList;
private final Metadata meta;
private List<Metadata> extraData;
private boolean cancel;
public MetaHandlerEventImpl(UserConnection connection, @Nullable EntityType entityType, int entityId, Metadata meta, List<Metadata> metadataList) {
public MetaHandlerEventImpl(UserConnection connection, @Nullable TrackedEntity trackedEntity, int entityId, Metadata meta, List<Metadata> metadataList) {
this.connection = connection;
this.entityType = entityType;
this.trackedEntity = trackedEntity;
this.entityId = entityId;
this.meta = meta;
this.metadataList = metadataList;
@@ -54,8 +55,8 @@ public class MetaHandlerEventImpl implements MetaHandlerEvent {
}
@Override
public @Nullable EntityType entityType() {
return entityType;
public @Nullable TrackedEntity trackedEntity() {
return trackedEntity;
}
@Override
@@ -0,0 +1,215 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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.util;
import com.google.common.io.CharStreams;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.dump.DumpTemplate;
import com.viaversion.viaversion.dump.VersionInfo;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InvalidObjectException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class DumpUtil {
/**
* Creates a platform dump and posts it to ViaVersion's dump server asychronously.
* May complete exceptionally with {@link DumpException}.
*
* @param playerToSample uuid of the player to include the pipeline of
* @return completable future that completes with the url of the dump
*/
public static CompletableFuture<String> postDump(@Nullable final UUID playerToSample) {
final VersionInfo version = new VersionInfo(
System.getProperty("java.version"),
System.getProperty("os.name"),
Via.getAPI().getServerVersion().lowestSupportedVersion(),
Via.getManager().getProtocolManager().getSupportedVersions(),
Via.getPlatform().getPlatformName(),
Via.getPlatform().getPlatformVersion(),
Via.getPlatform().getPluginVersion(),
com.viaversion.viaversion.util.VersionInfo.getImplementationVersion(),
Via.getManager().getSubPlatforms()
);
final Map<String, Object> configuration = Via.getPlatform().getConfigurationProvider().getValues();
final DumpTemplate template = new DumpTemplate(version, configuration, Via.getPlatform().getDump(), Via.getManager().getInjector().getDump(), getPlayerSample(playerToSample));
final CompletableFuture<String> result = new CompletableFuture<>();
Via.getPlatform().runAsync(() -> {
final HttpURLConnection con;
try {
con = (HttpURLConnection) new URL("https://dump.viaversion.com/documents").openConnection();
} catch (final IOException e) {
Via.getPlatform().getLogger().log(Level.SEVERE, "Error when opening connection to ViaVersion dump service", e);
result.completeExceptionally(new DumpException(DumpErrorType.CONNECTION, e));
return;
}
try {
con.setRequestProperty("Content-Type", "application/json");
con.addRequestProperty("User-Agent", "ViaVersion-" + Via.getPlatform().getPlatformName() + "/" + version.getPluginVersion());
con.setRequestMethod("POST");
con.setDoOutput(true);
try (final OutputStream out = con.getOutputStream()) {
out.write(new GsonBuilder().setPrettyPrinting().create().toJson(template).getBytes(StandardCharsets.UTF_8));
}
if (con.getResponseCode() == 429) {
result.completeExceptionally(new DumpException(DumpErrorType.RATE_LIMITED));
return;
}
final String rawOutput;
try (final InputStream inputStream = con.getInputStream()) {
rawOutput = CharStreams.toString(new InputStreamReader(inputStream));
}
final JsonObject output = GsonUtil.getGson().fromJson(rawOutput, JsonObject.class);
if (!output.has("key")) {
throw new InvalidObjectException("Key is not given in Hastebin output");
}
result.complete(urlForId(output.get("key").getAsString()));
} catch (final Exception e) {
Via.getPlatform().getLogger().log(Level.SEVERE, "Error when posting ViaVersion dump", e);
result.completeExceptionally(new DumpException(DumpErrorType.POST, e));
printFailureInfo(con);
}
});
return result;
}
private static void printFailureInfo(final HttpURLConnection connection) {
try {
if (connection.getResponseCode() < 200 || connection.getResponseCode() > 400) {
try (final InputStream errorStream = connection.getErrorStream()) {
final String rawOutput = CharStreams.toString(new InputStreamReader(errorStream));
Via.getPlatform().getLogger().log(Level.SEVERE, "Page returned: " + rawOutput);
}
}
} catch (final IOException e) {
Via.getPlatform().getLogger().log(Level.SEVERE, "Failed to capture further info", e);
}
}
public static String urlForId(final String id) {
return String.format("https://dump.viaversion.com/%s", id);
}
private static JsonObject getPlayerSample(@Nullable final UUID uuid) {
final JsonObject playerSample = new JsonObject();
// Player versions
final JsonObject versions = new JsonObject();
playerSample.add("versions", versions);
final Map<ProtocolVersion, Integer> playerVersions = new TreeMap<>((o1, o2) -> ProtocolVersion.getIndex(o2) - ProtocolVersion.getIndex(o1));
for (final UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
final ProtocolVersion protocolVersion = ProtocolVersion.getProtocol(connection.getProtocolInfo().getProtocolVersion());
playerVersions.compute(protocolVersion, (v, num) -> num != null ? num + 1 : 1);
}
for (final Map.Entry<ProtocolVersion, Integer> entry : playerVersions.entrySet()) {
versions.addProperty(entry.getKey().getName(), entry.getValue());
}
final Set<List<String>> pipelines = new HashSet<>();
if (uuid != null) {
// Pipeline of sender
final UserConnection senderConnection = Via.getAPI().getConnection(uuid);
if (senderConnection != null && senderConnection.getChannel() != null) {
pipelines.add(senderConnection.getChannel().pipeline().names());
}
}
// Other pipelines if different ones are found (3 max)
for (final UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
if (connection.getChannel() == null) {
continue;
}
final List<String> names = connection.getChannel().pipeline().names();
if (pipelines.add(names) && pipelines.size() == 3) {
break;
}
}
int i = 0;
for (final List<String> pipeline : pipelines) {
final JsonArray senderPipeline = new JsonArray(pipeline.size());
for (final String name : pipeline) {
senderPipeline.add(name);
}
playerSample.add("pipeline-" + i++, senderPipeline);
}
return playerSample;
}
public static final class DumpException extends RuntimeException {
private final DumpErrorType errorType;
private DumpException(final DumpErrorType errorType, final Throwable cause) {
super(errorType.message(), cause);
this.errorType = errorType;
}
private DumpException(final DumpErrorType errorType) {
super(errorType.message());
this.errorType = errorType;
}
public DumpErrorType errorType() {
return errorType;
}
}
public enum DumpErrorType {
CONNECTION("Failed to dump, please check the console for more information"),
RATE_LIMITED("Please wait before creating another dump"),
POST("Failed to dump, please check the console for more information");
private final String message;
DumpErrorType(final String message) {
this.message = message;
}
public String message() {
return message;
}
}
}
Loaded 100 of 115 files, more files were not shown because too many files have changed in this diff. Show more