Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e42a368e0 | ||
|
|
27982efd29 | ||
|
|
c3b343fbc8 | ||
|
|
9bedb6c741 | ||
|
|
873f4dd298 | ||
|
|
693990644f | ||
|
|
b2680d76a6 | ||
|
|
db935b454f | ||
|
|
c207a9bfff | ||
|
|
f5bd041c7b | ||
|
|
07d89b24af | ||
|
|
1d0c0c527c | ||
|
|
7a9cb86563 | ||
|
|
87b1919863 | ||
|
|
d164e413c2 | ||
|
|
76d7db044f | ||
|
|
046ebb8ecc | ||
|
|
bccde04ace | ||
|
|
9385f03722 | ||
|
|
b48fc17ead | ||
|
|
b50d0075ca | ||
|
|
84d006c71d | ||
|
|
212da9c46c | ||
|
|
8edb7048a1 | ||
|
|
abe3c4b0c9 | ||
|
|
c6d3c97c65 | ||
|
|
13c6a9d612 | ||
|
|
7d2ef743ac | ||
|
|
598d0f96d8 | ||
|
|
cbc22922d3 | ||
|
|
49783d36c2 | ||
|
|
70d91b4f87 | ||
|
|
17f3d13267 | ||
|
|
559674bf58 | ||
|
|
fbc298bc30 | ||
|
|
e36902c441 | ||
|
|
805c78989a | ||
|
|
85e416171c | ||
|
|
b55c0d0783 | ||
|
|
33b4b3835f | ||
|
|
8ccd772105 | ||
|
|
8677c632df | ||
|
|
560476c56f | ||
|
|
20bbe7cec6 | ||
|
|
1a002486d0 | ||
|
|
26fcdfe3bc | ||
|
|
4af98b220c | ||
|
|
f91fcb4ea8 | ||
|
|
b3060e0dc8 | ||
|
|
808a9a7d82 | ||
|
|
32c43e6023 | ||
|
|
4c9c50f939 | ||
|
|
ac1b35379e | ||
|
|
b743589d77 | ||
|
|
9dad0a0d5e | ||
|
|
f5b49bc9ce | ||
|
|
b3612a68b0 | ||
|
|
7385db02c2 | ||
|
|
501d2ec3a2 | ||
|
|
1217a9b7e2 | ||
|
|
6740c5c6dc | ||
|
|
44e8056fb3 | ||
|
|
d1da412cf7 | ||
|
|
6534a5414e | ||
|
|
91344a0b50 | ||
|
|
cdd9ff4469 | ||
|
|
d88b76db85 | ||
|
|
bc1d57853c | ||
|
|
27d6c12556 | ||
|
|
a76dd1ba49 | ||
|
|
1a4388b3e7 | ||
|
|
f1b81f77d6 | ||
|
|
d6edec3a55 | ||
|
|
094f4c6d58 | ||
|
|
c57d0f60f9 | ||
|
|
dd5cd1ca48 | ||
|
|
ab4a565700 | ||
|
|
f8ac54e237 | ||
|
|
089e07fe49 | ||
|
|
1d3b8f802c | ||
|
|
27283e48a2 | ||
|
|
d22584afbc | ||
|
|
249ab01425 | ||
|
|
851097de44 | ||
|
|
c095a51f7c | ||
|
|
5e62b7001e | ||
|
|
a79533bf72 | ||
|
|
e3510d01d6 | ||
|
|
8cc4ba168a | ||
|
|
264a21d092 | ||
|
|
e2585db350 | ||
|
|
a2f7a52d31 | ||
|
|
d04121202d | ||
|
|
95517ade2c | ||
|
|
c9441de7e4 | ||
|
|
dc946cb845 | ||
|
|
5fa30d9adb | ||
|
|
a7043424ec | ||
|
|
e1b3d21732 | ||
|
|
8ffc056a7e | ||
|
|
7c274fb03d | ||
|
|
3b533b5138 | ||
|
|
5665510f7e | ||
|
|
47faa21ef4 | ||
|
|
575f457830 | ||
|
|
d5bf7909e8 | ||
|
|
fdf90b7931 | ||
|
|
e3305e4eae | ||
|
|
15fe8d394e | ||
|
|
082db38c40 | ||
|
|
be54dc41d1 | ||
|
|
487e79c043 | ||
|
|
ef6390a0b8 | ||
|
|
9d9fc65633 | ||
|
|
6eac61c912 | ||
|
|
d89362a271 | ||
|
|
0a407069fa | ||
|
|
661fdb1127 | ||
|
|
9e6c0f72a8 | ||
|
|
70ad2613f1 | ||
|
|
9052946c45 | ||
|
|
3ddc43bf7b | ||
|
|
cdb757eae5 | ||
|
|
50a3e03649 | ||
|
|
957284930e | ||
|
|
198a24371c | ||
|
|
bcc994b0dd | ||
|
|
f870bd5a32 | ||
|
|
a14abd6f4f | ||
|
|
2456c3cf17 | ||
|
|
9828e4ae1d | ||
|
|
030aa887c7 | ||
|
|
ba624a2b48 | ||
|
|
e7582cb1ec | ||
|
|
82bf11fcbc | ||
|
|
ac6e8b1709 | ||
|
|
00cc545795 | ||
|
|
c7ee816aba | ||
|
|
e7f85e67f6 | ||
|
|
d239d2f967 | ||
|
|
63970c6d82 | ||
|
|
4cffdd4f64 | ||
|
|
cb3f0291b7 | ||
|
|
edaffad045 | ||
|
|
172f89940a | ||
|
|
fb4bc397c6 | ||
|
|
e595344941 | ||
|
|
05dbc53fd0 | ||
|
|
0374da573b | ||
|
|
8e64fb8c21 | ||
|
|
0f55402171 | ||
|
|
acb7372eb7 | ||
|
|
057ab050ea | ||
|
|
a01997fe53 | ||
|
|
e4adbf4a1a | ||
|
|
261d68c58e | ||
|
|
38617d1f61 | ||
|
|
cc5bcb8a64 | ||
|
|
b6794f5597 | ||
|
|
18f77308f5 | ||
|
|
6bb473ea8d | ||
|
|
7984f6400c | ||
|
|
3498d093b0 | ||
|
|
a3db32758c | ||
|
|
45952d3759 | ||
|
|
c552b12696 | ||
|
|
0ae9d9dbbf | ||
|
|
8852e3b3a3 | ||
|
|
a496b2d3a8 | ||
|
|
6198dbfe70 | ||
|
|
a81d52a54f | ||
|
|
cf20e8ac5f | ||
|
|
4f8a248c6b | ||
|
|
57d58db35e | ||
|
|
c214ca02e8 | ||
|
|
6b8b0e1771 | ||
|
|
e6671513f2 | ||
|
|
4c7b50d6fb | ||
|
|
1c9229a2fc | ||
|
|
d11c18c024 | ||
|
|
dc4b5b4d35 | ||
|
|
517299d673 | ||
|
|
a8baa7af50 | ||
|
|
a1f847c905 | ||
|
|
f3ea43b1eb | ||
|
|
551b5d0654 | ||
|
|
9f39662df6 | ||
|
|
7ea2156f37 | ||
|
|
5032e7d07e | ||
|
|
c7c6ee9fbc | ||
|
|
4431ca63c2 | ||
|
|
1fa7211cf0 | ||
|
|
de576795dc | ||
|
|
f6286bcc25 | ||
|
|
8276b212e9 | ||
|
|
aabd9247a6 | ||
|
|
997c2ad6a1 | ||
|
|
233a2ba5df | ||
|
|
ed0d5a7cfb | ||
|
|
87f083460c | ||
|
|
2d6dedefb8 | ||
|
|
8adbf36a3a | ||
|
|
8c706b665c | ||
|
|
2e3677a592 | ||
|
|
3602c5087b | ||
|
|
5c84b3d8cc | ||
|
|
7defe7129f | ||
|
|
995dc135b8 | ||
|
|
3f64fb7d5f | ||
|
|
ac8f7a00ae | ||
|
|
6893f34289 | ||
|
|
0a69d62cbe | ||
|
|
5e0096aa2c | ||
|
|
7326205495 | ||
|
|
a8212db7f2 | ||
|
|
2905d77206 | ||
|
|
cb8ec2ce20 | ||
|
|
09f484f956 | ||
|
|
c48922eb3e | ||
|
|
b701a0ef67 | ||
|
|
eb50f4e907 | ||
|
|
6480ea34fc | ||
|
|
511941462f | ||
|
|
34cc783cac | ||
|
|
ecc79b4f49 | ||
|
|
76f10c463f | ||
|
|
41d5144b57 | ||
|
|
de196f2e4f | ||
|
|
0acc2f95fb | ||
|
|
d16eb9b27e | ||
|
|
8760b53a0a | ||
|
|
a40182186f | ||
|
|
ef2b173749 | ||
|
|
75c9284cda | ||
|
|
2abecba7bf | ||
|
|
fb6055cbfe | ||
|
|
639c9ea8c1 | ||
|
|
1872845d2b | ||
|
|
93755c6732 | ||
|
|
68973fd136 | ||
|
|
1a0bd37a01 | ||
|
|
3551779600 | ||
|
|
b8deddcd2c | ||
|
|
c49bf89ead | ||
|
|
4ff4fe2ab2 | ||
|
|
1b99e2cf57 | ||
|
|
55830b0dfb | ||
|
|
e61f56b2e0 | ||
|
|
95ea95be13 | ||
|
|
d72b7fc4df | ||
|
|
0992c74351 | ||
|
|
081c23ad8b | ||
|
|
258f0fc636 | ||
|
|
c4e3864fc0 | ||
|
|
405b6e8047 | ||
|
|
9847912128 | ||
|
|
d89a34cca3 | ||
|
|
6197138380 | ||
|
|
f5f4876182 | ||
|
|
293c1229ff | ||
|
|
ba60f10744 | ||
|
|
c0493de4df | ||
|
|
46b2f69b01 | ||
|
|
f926113c66 | ||
|
|
0e4df51613 | ||
|
|
c5f72dc728 | ||
|
|
cee5b1ee42 | ||
|
|
f1b9cd2e56 | ||
|
|
7807e6d9c4 | ||
|
|
4b354f806d | ||
|
|
41707ca867 | ||
|
|
5bca3a97ec | ||
|
|
3679aa618f | ||
|
|
e9d96f2daf | ||
|
|
f371419396 | ||
|
|
f32d3211ed | ||
|
|
45f99d4c31 | ||
|
|
71a9c90fe9 | ||
|
|
406500b0bd | ||
|
|
9257298e17 | ||
|
|
0376602894 | ||
|
|
95ceabb495 | ||
|
|
6c8ccb1b1e | ||
|
|
f351dd03e7 | ||
|
|
c1e77a49b6 | ||
|
|
9adf6040fa | ||
|
|
3e320618f9 | ||
|
|
6c1de57d32 | ||
|
|
7694d311d1 | ||
|
|
eee1fd941e | ||
|
|
9c5c181389 | ||
|
|
b4b85b1602 | ||
|
|
f6bb1b48f5 | ||
|
|
4ce846a899 | ||
|
|
ef08117f34 | ||
|
|
4b582a7279 | ||
|
|
ad53c1ed7f | ||
|
|
6e46d018a4 | ||
|
|
6beacf9804 | ||
|
|
e15c8d7257 | ||
|
|
3518804cbf | ||
|
|
101f86c8cd | ||
|
|
22c2435656 | ||
|
|
af53275ab9 | ||
|
|
eccbb567e8 | ||
|
|
bae3599388 | ||
|
|
88ab4e53a7 | ||
|
|
25fa2eabbb | ||
|
|
e12d8ec466 | ||
|
|
a7022bddf8 | ||
|
|
523c27237d | ||
|
|
147891f76b | ||
|
|
a5ba52ce05 | ||
|
|
ccb2a4756f | ||
|
|
f5a96b791d | ||
|
|
c628a8a23f | ||
|
|
80f63d79ef | ||
|
|
b225afe6ef | ||
|
|
4e1625b017 | ||
|
|
5c4867365e | ||
|
|
3549a823cb | ||
|
|
23a00d2555 | ||
|
|
57931e7231 | ||
|
|
9e19e0c8af | ||
|
|
74461609c1 | ||
|
|
7d3e6bc7cb | ||
|
|
175606f320 | ||
|
|
b226e94981 | ||
|
|
7d0687aa4a | ||
|
|
d152b8b9e1 | ||
|
|
1fbd561aec | ||
|
|
a8295c9de0 | ||
|
|
e894a3c733 | ||
|
|
a2f8f28010 | ||
|
|
704c876772 | ||
|
|
0233e2ab27 | ||
|
|
5ae8a35740 | ||
|
|
ed766eb656 | ||
|
|
9b6a9fca3d | ||
|
|
615128eba2 | ||
|
|
8f3b938e39 | ||
|
|
625a1d2191 | ||
|
|
8a35c0235e | ||
|
|
69e8ddcbf6 | ||
|
|
ce8a504750 | ||
|
|
56d5f8eec6 | ||
|
|
7a98cdd28f | ||
|
|
ccda9f11c8 | ||
|
|
7e133a0be8 | ||
|
|
2197b938f8 | ||
|
|
c300c47a14 | ||
|
|
89427cb2da | ||
|
|
fce7f1740e | ||
|
|
0357d8e6aa | ||
|
|
55335944e3 | ||
|
|
c0b2985343 | ||
|
|
91ae5990b1 | ||
|
|
33f1a8975c | ||
|
|
9b28129187 | ||
|
|
81d89d3839 | ||
|
|
e19a2920b9 | ||
|
|
7cff20f218 | ||
|
|
131517105e |
No files matched your search
@@ -1,3 +1,24 @@
|
||||
License:
|
||||
--------
|
||||
You may modify this work, all rights are still owned by myself excluding any files in the spacebase package as they belong to the respected contributors. Please credit the original author (myself) when forking etc. (or if it helped you understand the new protocol or netty!)
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016
|
||||
|
||||
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.
|
||||
@@ -1,34 +1,34 @@
|
||||
# ViaVersion 0.6.6
|
||||
# ViaVersion 0.9.8
|
||||
[](https://travis-ci.org/MylesIsCool/ViaVersion)
|
||||
**Allows the connection of 1.9 clients to 1.8**
|
||||
[](https://gitter.im/MylesIsCool/ViaVersion)
|
||||
|
||||
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
|
||||
**Allows the connection of higher client versions to lower server versions**
|
||||
|
||||
###*Don't use late bind*
|
||||
Allows 1.10, 1.9 on 1.8 server and 1.10 on a 1.9 server.
|
||||
|
||||
|
||||
This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
|
||||
**User Docs:** https://docs.viaversion.com/display/VIAVERSION/
|
||||
|
||||
**Contributor Chat:** https://gitter.im/MylesIsCool/ViaVersion
|
||||
|
||||
|
||||
|
||||
Sources:
|
||||
--------
|
||||
|
||||
**wiki.vg** (Protocol info, though I did have to find a lot myself)
|
||||
|
||||
**MCProtocolLib** (used for chunk reading & some of writing.)
|
||||
**Burger** We use this tool to get protocol and other useful information for new / undocumented Minecraft versions
|
||||
|
||||
**OpenNBT** (used for slot rewriting)
|
||||
|
||||
|
||||
|
||||
Contributors:
|
||||
All Time Contributors:
|
||||
--------
|
||||
|
||||
**Myself** (harhar)
|
||||
|
||||
**Matsv/StamBoom**
|
||||
**Matsv**
|
||||
|
||||
**HugoDaBosss**
|
||||
|
||||
@@ -42,6 +42,10 @@ Contributors:
|
||||
|
||||
**lenis0012**
|
||||
|
||||
**fuzzy_bot**
|
||||
|
||||
**joserobjr**
|
||||
|
||||
|
||||
|
||||
Releases / Dev Builds:
|
||||
@@ -53,18 +57,18 @@ https://www.spigotmc.org/resources/viaversion.19254/history
|
||||
|
||||
----------
|
||||
|
||||
You can find unofficial dev builds here:
|
||||
You can find official dev builds here:
|
||||
|
||||
**fillefilip8's Build Server:** http://build.fillecraft.com:8080/job/ViaVersion
|
||||
**Jenkins:** https://ci.viaversion.com
|
||||
|
||||
**sgdc3's Build Server:** http://ci.xephi.fr/job/ViaVersion/
|
||||
**sgdc3's Maven:** http://ci.xephi.fr/plugin/repository/everything/
|
||||
**JavaDocs:** https://jd.viaversion.com
|
||||
|
||||
**Maven Repo:** https://repo.viaversion.com
|
||||
|
||||
License:
|
||||
--------
|
||||
|
||||
You may modify this work, all rights are still owned by myself excluding any files in the spacebase package as they belong to the respected contributors. Please credit the original author (myself) when forking etc. (or if it helped you understand the new protocol or netty!)
|
||||
|
||||
MIT License, see LICENSE for more details.
|
||||
|
||||
Thanks!
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion</artifactId>
|
||||
<version>0.6.7-SNAPSHOT</version>
|
||||
<version>0.9.8</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ViaVersion</name>
|
||||
@@ -33,6 +33,7 @@
|
||||
<build>
|
||||
<finalName>${project.name}-${project.version}</finalName>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<!-- <testSourceDirectory>src/test/java</testSourceDirectory> -->
|
||||
|
||||
<resources>
|
||||
@@ -105,6 +106,10 @@
|
||||
<pattern>com.google.gson</pattern>
|
||||
<shadedPattern>us.myles.viaversion.libs.gson</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.javassist</pattern>
|
||||
<shadedPattern>us.myles.viaversion.libs.javassist</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
@@ -131,6 +136,12 @@
|
||||
<id>spacehq-repo</id>
|
||||
<url>https://repo.spacehq.org/content/repositories/releases/</url>
|
||||
</repository>
|
||||
|
||||
<!-- Bungee repo -->
|
||||
<repository>
|
||||
<id>bungeecord-repo</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@@ -157,6 +168,15 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Javassist (Bytecode Library) -->
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>3.20.0-GA</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- NBT Edit Library -->
|
||||
<dependency>
|
||||
<groupId>org.spacehq</groupId>
|
||||
@@ -191,5 +211,13 @@
|
||||
<version>1.16.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Bungeecord-->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.9-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,18 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
import static net.md_5.bungee.api.ChatColor.RED;
|
||||
|
||||
public class Bungee extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getLogger().severe(RED + "===============================================");
|
||||
getLogger().severe(RED + "ViaVersion is NOT a Bungeecord plugin");
|
||||
getLogger().severe(RED + "Install this plugin on all your spigot/bukkit");
|
||||
getLogger().severe(RED + "servers and use the latest Bungeecord version");
|
||||
getLogger().severe(RED + "to make ViaVersion work with BungeeCord.");
|
||||
getLogger().severe(RED + "===============================================");
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.chunks.ChunkManager;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ConnectionInfo {
|
||||
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 final SocketChannel channel;
|
||||
private final ChunkManager chunkManager;
|
||||
private Object lastPacket;
|
||||
private java.util.UUID UUID;
|
||||
private State state = State.HANDSHAKE;
|
||||
private String openWindow;
|
||||
private int protocol = 0;
|
||||
private int compression = 0;
|
||||
private int entityID;
|
||||
private boolean active = true;
|
||||
private String username;
|
||||
private long nextIdlePacket = 0L;
|
||||
|
||||
public ConnectionInfo(SocketChannel socketChannel) {
|
||||
this.channel = socketChannel;
|
||||
this.chunkManager = new ChunkManager(this);
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return UUID == null ? null : Bukkit.getPlayer(UUID);
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
this.nextIdlePacket = System.currentTimeMillis() + IDLE_PACKET_DELAY; // Update every 50 ticks
|
||||
}
|
||||
|
||||
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
|
||||
final ChannelHandler handler = channel.pipeline().get("encoder");
|
||||
if (currentThread) {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
} else {
|
||||
channel.eventLoop().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void sendRawPacket(final ByteBuf packet) {
|
||||
sendRawPacket(packet, false);
|
||||
}
|
||||
|
||||
public void closeWindow() {
|
||||
this.openWindow = null;
|
||||
}
|
||||
|
||||
public void incrementIdlePacket() {
|
||||
// Notify of next update
|
||||
// Allow a maximum lag spike of 1 second (20 ticks/updates)
|
||||
this.nextIdlePacket = Math.max(nextIdlePacket + IDLE_PACKET_DELAY, System.currentTimeMillis() - IDLE_PACKET_DELAY * IDLE_PACKET_LIMIT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import us.myles.ViaVersion.api.ViaVersionConfig;
|
||||
import us.myles.ViaVersion.util.Configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class ViaConfig implements ViaVersionConfig {
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
public ViaConfig(ViaVersionPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
generateConfig();
|
||||
}
|
||||
|
||||
public void generateConfig() {
|
||||
File file = new File(plugin.getDataFolder(), "config.yml");
|
||||
if (file.exists()) {
|
||||
// Update conf options
|
||||
Configuration oldConfig = new Configuration(file);
|
||||
oldConfig.reload(false); // Load current options from conf
|
||||
file.delete(); // Delete old conf
|
||||
plugin.saveDefaultConfig(); // Generate new conf
|
||||
Configuration newConfig = new Configuration(file);
|
||||
newConfig.reload(true); // Load default options
|
||||
for (String key : oldConfig.getKeys(false)) {
|
||||
// Set option in new conf if exists
|
||||
if (newConfig.contains(key)) {
|
||||
newConfig.set(key, oldConfig.get(key));
|
||||
}
|
||||
}
|
||||
newConfig.save();
|
||||
} else {
|
||||
plugin.saveDefaultConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCheckForUpdates() {
|
||||
return plugin.getConfig().getBoolean("checkforupdates", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreventCollision() {
|
||||
return plugin.getConfig().getBoolean("prevent-collision", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewEffectIndicator() {
|
||||
return plugin.getConfig().getBoolean("use-new-effect-indicator", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowNewDeathMessages() {
|
||||
return plugin.getConfig().getBoolean("use-new-deathmessages", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuppressMetadataErrors() {
|
||||
return plugin.getConfig().getBoolean("suppress-metadata-errors", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShieldBlocking() {
|
||||
return plugin.getConfig().getBoolean("shield-blocking", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHologramPatch() {
|
||||
return plugin.getConfig().getBoolean("hologram-patch", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBossbarPatch() {
|
||||
return plugin.getConfig().getBoolean("bossbar-patch", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBossbarAntiflicker() {
|
||||
return plugin.getConfig().getBoolean("bossbar-anti-flicker", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnknownEntitiesSuppressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHologramYOffset() {
|
||||
return plugin.getConfig().getDouble("hologram-y", -0.96D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlockBreakPatch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPPS() {
|
||||
return plugin.getConfig().getInt("max-pps", 140);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaxPPSKickMessage() {
|
||||
return plugin.getConfig().getString("max-pps-kick-msg", "Sending packets too fast? lag?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrackingPeriod() {
|
||||
return plugin.getConfig().getInt("tracking-period", 6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWarningPPS() {
|
||||
return plugin.getConfig().getInt("tracking-warning-pps", 120);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxWarnings() {
|
||||
return plugin.getConfig().getInt("tracking-max-warnings", 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaxWarningsKickMessage() {
|
||||
return plugin.getConfig().getString("tracking-max-kick-msg", "You are sending too many packets, :(");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAntiXRay() {
|
||||
return plugin.getConfig().getBoolean("anti-xray-patch", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSendSupportedVersions() {
|
||||
return plugin.getConfig().getBoolean("send-supported-versions", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStimulatePlayerTick() {
|
||||
return plugin.getConfig().getBoolean("simulate-pt", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemCache() {
|
||||
return plugin.getConfig().getBoolean("item-cache", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNMSPlayerTicking() {
|
||||
return plugin.getConfig().getBoolean("nms-player-ticking", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplacePistons() {
|
||||
return plugin.getConfig().getBoolean("replace-pistons", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPistonReplacementId() {
|
||||
return plugin.getConfig().getInt("replacement-piston-id", 0);
|
||||
}
|
||||
|
||||
public boolean isAutoTeam() {
|
||||
// Collision has to be enabled first
|
||||
return isPreventCollision() && plugin.getConfig().getBoolean("auto-team", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForceJsonTransform() {
|
||||
return plugin.getConfig().getBoolean("force-json-transform", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getBlockedProtocols() {
|
||||
return plugin.getConfig().getIntegerList("block-protocols");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBlockedDisconnectMsg() {
|
||||
return plugin.getConfig().getString("block-disconnect-msg", "You are using an unsupported Minecraft version!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReloadDisconnectMsg() {
|
||||
return plugin.getConfig().getString("reload-disconnect-msg", "Server reload, please rejoin!");
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package us.myles.ViaVersion;
|
||||
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ViaIdleThread extends BukkitRunnable {
|
||||
private final Map<UUID, ConnectionInfo> portedPlayers;
|
||||
private final Class<?> idlePacketClass;
|
||||
|
||||
public ViaIdleThread(Map<UUID, ConnectionInfo> portedPlayers) {
|
||||
this.portedPlayers = portedPlayers;
|
||||
try {
|
||||
this.idlePacketClass = ReflectionUtil.nms("PacketPlayInFlying");
|
||||
} catch(ClassNotFoundException e) {
|
||||
throw new RuntimeException("Couldn't find player idle packet, help!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for(ConnectionInfo info : portedPlayers.values()) {
|
||||
long nextIdleUpdate = info.getNextIdlePacket();
|
||||
if(nextIdleUpdate <= System.currentTimeMillis()) {
|
||||
try {
|
||||
Object packet = idlePacketClass.newInstance();
|
||||
info.getChannel().pipeline().fireChannelRead(packet);
|
||||
} catch(InstantiationException | IllegalAccessException e) {
|
||||
System.out.println("Failed to create idle packet.");
|
||||
if(ViaVersion.getInstance().isDebug()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
info.incrementIdlePacket();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,36 +5,37 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.ViaVersionAPI;
|
||||
import us.myles.ViaVersion.api.boss.BossBar;
|
||||
import us.myles.ViaVersion.api.boss.BossColor;
|
||||
import us.myles.ViaVersion.api.boss.BossStyle;
|
||||
import us.myles.ViaVersion.armor.ArmorListener;
|
||||
import us.myles.ViaVersion.api.command.ViaVersionCommand;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
import us.myles.ViaVersion.boss.ViaBossBar;
|
||||
import us.myles.ViaVersion.commands.ViaVersionCommand;
|
||||
import us.myles.ViaVersion.classgenerator.ClassGenerator;
|
||||
import us.myles.ViaVersion.commands.ViaCommandHandler;
|
||||
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
|
||||
import us.myles.ViaVersion.listeners.CommandBlockListener;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
import us.myles.ViaVersion.update.UpdateListener;
|
||||
import us.myles.ViaVersion.update.UpdateUtil;
|
||||
import us.myles.ViaVersion.util.Configuration;
|
||||
import us.myles.ViaVersion.util.ConcurrentList;
|
||||
import us.myles.ViaVersion.util.ListWrapper;
|
||||
import us.myles.ViaVersion.util.ProtocolSupportUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -42,127 +43,214 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
|
||||
private final Map<UUID, ConnectionInfo> portedPlayers = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, UserConnection> portedPlayers = new ConcurrentHashMap<>();
|
||||
private List<ChannelFuture> injectedFutures = new ArrayList<>();
|
||||
private List<Pair<Field, Object>> injectedLists = new ArrayList<>();
|
||||
private ViaCommandHandler commandHandler;
|
||||
private boolean debug = false;
|
||||
private boolean compatSpigotBuild = false;
|
||||
private boolean spigot = true;
|
||||
private boolean lateBind = false;
|
||||
private boolean protocolSupport = false;
|
||||
@Getter
|
||||
private ViaConfig conf;
|
||||
|
||||
public static ItemStack getHandItem(final ConnectionInfo info) {
|
||||
try {
|
||||
return Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<ItemStack>() {
|
||||
@Override
|
||||
public ItemStack call() throws Exception {
|
||||
if (info.getPlayer() != null) {
|
||||
return info.getPlayer().getItemInHand();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error fetching hand item: " + e.getClass().getName());
|
||||
if (ViaVersion.getInstance().isDebug())
|
||||
public ViaVersionPlugin() {
|
||||
// Check if we're using protocol support too
|
||||
protocolSupport = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
|
||||
|
||||
if (protocolSupport) {
|
||||
getLogger().info("Patching to prevent concurrency issues...");
|
||||
try {
|
||||
patchLists();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
public void onLoad() {
|
||||
// Config magic
|
||||
conf = new ViaConfig(this);
|
||||
ViaVersion.setInstance(this);
|
||||
generateConfig();
|
||||
|
||||
// Handle reloads
|
||||
if (System.getProperty("ViaVersion") != null) {
|
||||
getLogger().severe("ViaVersion is already loaded, we don't support reloads. Please reboot if you wish to update.");
|
||||
getLogger().severe("Some features may not work.");
|
||||
return;
|
||||
if (Bukkit.getPluginManager().getPlugin("ProtocolLib") != null) {
|
||||
getLogger().severe("ViaVersion is already loaded, we're going to kick all the players... because otherwise we'll crash because of ProtocolLib.");
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', getConf().getReloadDisconnectMsg()));
|
||||
}
|
||||
|
||||
} else {
|
||||
getLogger().severe("ViaVersion is already loaded, this should work fine... Otherwise reboot the server!!!");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
|
||||
injectPacketHandler();
|
||||
if (getConfig().getBoolean("simulate-pt", true))
|
||||
new ViaIdleThread(portedPlayers).runTaskTimerAsynchronously(this, 1L, 1L); // Updates player's idle status
|
||||
// Spigot detector
|
||||
try {
|
||||
Class.forName("org.spigotmc.SpigotConfig");
|
||||
} catch (ClassNotFoundException e) {
|
||||
spigot = false;
|
||||
}
|
||||
|
||||
if (getConfig().getBoolean("checkforupdates"))
|
||||
UpdateUtil.sendUpdateMessage(this);
|
||||
// Check if it's a spigot build with a protocol mod
|
||||
try {
|
||||
compatSpigotBuild = ReflectionUtil.nms("PacketEncoder").getDeclaredField("version") != null;
|
||||
} catch (Exception e) {
|
||||
compatSpigotBuild = false;
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent e) {
|
||||
removePortedClient(e.getPlayer().getUniqueId());
|
||||
}
|
||||
}, this);
|
||||
// Generate classes needed (only works if it's compat or ps)
|
||||
ClassGenerator.generate();
|
||||
lateBind = !isBinded();
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new ArmorListener(this), this);
|
||||
Bukkit.getPluginManager().registerEvents(new CommandBlockListener(this), this);
|
||||
Bukkit.getPluginManager().registerEvents(new UpdateListener(this), this);
|
||||
|
||||
getCommand("viaversion").setExecutor(new ViaVersionCommand(this));
|
||||
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
|
||||
if (!lateBind)
|
||||
injectPacketHandler();
|
||||
}
|
||||
|
||||
public void generateConfig() {
|
||||
File file = new File(getDataFolder(), "config.yml");
|
||||
if (file.exists()) {
|
||||
// Update config options
|
||||
Configuration oldConfig = new Configuration(file);
|
||||
oldConfig.reload(false); // Load current options from config
|
||||
file.delete(); // Delete old config
|
||||
saveDefaultConfig(); // Generate new config
|
||||
Configuration newConfig = new Configuration(file);
|
||||
newConfig.reload(true); // Load default options
|
||||
for (String key : oldConfig.getKeys(false)) {
|
||||
// Set option in new config if exists
|
||||
if (newConfig.contains(key)) {
|
||||
newConfig.set(key, oldConfig.get(key));
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (lateBind)
|
||||
injectPacketHandler();
|
||||
if (conf.isCheckForUpdates())
|
||||
UpdateUtil.sendUpdateMessage(this);
|
||||
// Gather version :)
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
gatherProtocolVersion();
|
||||
// Check if there are any pipes to this version
|
||||
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
|
||||
getLogger().info("ViaVersion detected protocol version: " + ProtocolRegistry.SERVER_PROTOCOL);
|
||||
if (!ProtocolRegistry.isWorkingPipe()) {
|
||||
getLogger().warning("ViaVersion will not function on the current protocol.");
|
||||
}
|
||||
}
|
||||
ProtocolRegistry.refreshVersions();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new UpdateListener(this), this);
|
||||
|
||||
getCommand("viaversion").setExecutor(commandHandler = new ViaCommandHandler());
|
||||
getCommand("viaversion").setTabCompleter(commandHandler);
|
||||
|
||||
// Register Protocol Listeners
|
||||
ProtocolRegistry.registerListeners();
|
||||
|
||||
// Warn them if they have anti-xray on and they aren't using spigot
|
||||
if (conf.isAntiXRay() && !spigot) {
|
||||
getLogger().info("You have anti-xray on in your config, since you're not using spigot it won't fix xray!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().info("ViaVersion is disabling, if this is a reload it may not work.");
|
||||
uninject();
|
||||
}
|
||||
|
||||
public void gatherProtocolVersion() {
|
||||
try {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Class<?> pingClazz = ReflectionUtil.nms("ServerPing");
|
||||
Object ping = null;
|
||||
// Search for ping method
|
||||
for (Field f : serverClazz.getDeclaredFields()) {
|
||||
if (f.getType() != null) {
|
||||
if (f.getType().getSimpleName().equals("ServerPing")) {
|
||||
f.setAccessible(true);
|
||||
ping = f.get(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
newConfig.save();
|
||||
} else {
|
||||
saveDefaultConfig();
|
||||
if (ping != null) {
|
||||
Object serverData = null;
|
||||
for (Field f : pingClazz.getDeclaredFields()) {
|
||||
if (f.getType() != null) {
|
||||
if (f.getType().getSimpleName().endsWith("ServerData")) {
|
||||
f.setAccessible(true);
|
||||
serverData = f.get(ping);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (serverData != null) {
|
||||
int protocolVersion = -1;
|
||||
for (Field f : serverData.getClass().getDeclaredFields()) {
|
||||
if (f.getType() != null) {
|
||||
if (f.getType() == int.class) {
|
||||
f.setAccessible(true);
|
||||
protocolVersion = (int) f.get(serverData);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (protocolVersion != -1) {
|
||||
ProtocolRegistry.SERVER_PROTOCOL = protocolVersion;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// We couldn't work it out... We'll just use ping and hope for the best...
|
||||
}
|
||||
}
|
||||
|
||||
public Object getServerConnection() throws Exception {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Object connection = null;
|
||||
for (Method m : serverClazz.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != null) {
|
||||
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
|
||||
if (m.getParameterTypes().length == 0) {
|
||||
connection = m.invoke(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void injectPacketHandler() {
|
||||
try {
|
||||
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
|
||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||
Object connection = null;
|
||||
for (Method m : serverClazz.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != null) {
|
||||
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
|
||||
if (m.getParameterTypes().length == 0) {
|
||||
connection = m.invoke(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Object connection = getServerConnection();
|
||||
if (connection == null) {
|
||||
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
|
||||
return;
|
||||
}
|
||||
if (connection != null) {
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
// Inject the list
|
||||
List wrapper = new ListWrapper((List) value) {
|
||||
@Override
|
||||
public synchronized void handleAdd(Object o) {
|
||||
synchronized (this) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
inject((ChannelFuture) o);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
field.set(connection, wrapper);
|
||||
// Iterate through current list
|
||||
synchronized (wrapper) {
|
||||
for (Object o : (List) value) {
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
// Inject the list
|
||||
List wrapper = new ListWrapper((List) value) {
|
||||
@Override
|
||||
public synchronized void handleAdd(Object o) {
|
||||
synchronized (this) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
inject((ChannelFuture) o);
|
||||
} else {
|
||||
break; // not the right list.
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
injectedLists.add(new Pair<>(field, connection));
|
||||
field.set(connection, wrapper);
|
||||
// Iterate through current list
|
||||
synchronized (wrapper) {
|
||||
for (Object o : (List) value) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
inject((ChannelFuture) o);
|
||||
} else {
|
||||
break; // not the right list.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,6 +261,54 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void patchLists() throws Exception {
|
||||
Object connection = getServerConnection();
|
||||
if (connection == null) {
|
||||
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
|
||||
return;
|
||||
}
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
if (!(value instanceof ConcurrentList)) {
|
||||
ConcurrentList list = new ConcurrentList();
|
||||
list.addAll((List) value);
|
||||
field.set(connection, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isBinded() {
|
||||
try {
|
||||
Object connection = getServerConnection();
|
||||
if (connection == null) {
|
||||
return false;
|
||||
}
|
||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(connection);
|
||||
if (value instanceof List) {
|
||||
// Inject the list
|
||||
synchronized (value) {
|
||||
for (Object o : (List) value) {
|
||||
if (o instanceof ChannelFuture) {
|
||||
return true;
|
||||
} else {
|
||||
break; // not the right list.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void inject(ChannelFuture future) {
|
||||
try {
|
||||
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
|
||||
@@ -181,16 +317,53 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
|
||||
|
||||
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
|
||||
injectedFutures.add(future);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// field not found
|
||||
throw new Exception("Unable to find childHandler, blame " + bootstrapAcceptor.getClass().getName());
|
||||
// let's find who to blame!
|
||||
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
|
||||
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
|
||||
PluginDescriptionFile yaml = ReflectionUtil.get(cl, "description", PluginDescriptionFile.class);
|
||||
throw new Exception("Unable to inject, due to " + bootstrapAcceptor.getClass().getName() + ", try without the plugin " + yaml.getName() + "?");
|
||||
} else {
|
||||
throw new Exception("Unable to find childHandler, weird server version? " + bootstrapAcceptor.getClass().getName());
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Have you got late-bind enabled with something else? (ProtocolLib?)");
|
||||
getLogger().severe("Have you got late-bind enabled with something else?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void uninject() {
|
||||
// TODO: Uninject from players currently online to prevent protocol lib issues.
|
||||
for (ChannelFuture future : injectedFutures) {
|
||||
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
|
||||
try {
|
||||
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||
if (oldInit instanceof ViaVersionInitializer) {
|
||||
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((ViaVersionInitializer) oldInit).getOriginal());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to remove injection... reload won't work with connections sorry");
|
||||
}
|
||||
}
|
||||
injectedFutures.clear();
|
||||
|
||||
for (Pair<Field, Object> pair : injectedLists) {
|
||||
try {
|
||||
Object o = pair.getKey().get(pair.getValue());
|
||||
if (o instanceof ListWrapper) {
|
||||
pair.getKey().set(pair.getValue(), ((ListWrapper) o).getOriginalList());
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
System.out.println("Failed to remove injection... reload might not work with connections sorry");
|
||||
}
|
||||
}
|
||||
|
||||
injectedLists.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPorted(Player player) {
|
||||
return isPorted(player.getUniqueId());
|
||||
@@ -199,8 +372,23 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
@Override
|
||||
public int getPlayerVersion(@NonNull Player player) {
|
||||
if (!isPorted(player))
|
||||
return 47;
|
||||
return portedPlayers.get(player.getUniqueId()).getProtocol();
|
||||
return getExternalVersion(player);
|
||||
return portedPlayers.get(player.getUniqueId()).get(ProtocolInfo.class).getProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerVersion(@NonNull UUID uuid) {
|
||||
if (!isPorted(uuid))
|
||||
return getExternalVersion(Bukkit.getPlayer(uuid));
|
||||
return portedPlayers.get(uuid).get(ProtocolInfo.class).getProtocolVersion();
|
||||
}
|
||||
|
||||
private int getExternalVersion(Player player) {
|
||||
if (!isProtocolSupport()) {
|
||||
return ProtocolRegistry.SERVER_PROTOCOL;
|
||||
} else {
|
||||
return ProtocolSupportUtil.getProtocolVersion(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,14 +401,22 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
return getDescription().getVersion();
|
||||
}
|
||||
|
||||
public UserConnection getConnection(UUID playerUUID) {
|
||||
return portedPlayers.get(playerUUID);
|
||||
}
|
||||
|
||||
public UserConnection getConnection(Player player) {
|
||||
return portedPlayers.get(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException {
|
||||
sendRawPacket(player.getUniqueId(), packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException {
|
||||
if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not on 1.9");
|
||||
ConnectionInfo ci = portedPlayers.get(uuid);
|
||||
if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not controlled by ViaVersion!");
|
||||
UserConnection ci = portedPlayers.get(uuid);
|
||||
ci.sendRawPacket(packet);
|
||||
}
|
||||
|
||||
@@ -243,46 +439,31 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
this.debug = value;
|
||||
}
|
||||
|
||||
public boolean isPreventCollision() {
|
||||
return getConfig().getBoolean("prevent-collision", true);
|
||||
@Override
|
||||
public ViaVersionCommand getCommandHandler() {
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
public boolean isNewEffectIndicator(){
|
||||
return getConfig().getBoolean("use-new-effect-indicator",true);
|
||||
@Override
|
||||
public boolean isCompatSpigotBuild() {
|
||||
return compatSpigotBuild;
|
||||
}
|
||||
|
||||
public boolean isSuppressMetadataErrors() {
|
||||
return getConfig().getBoolean("suppress-metadata-errors", false);
|
||||
@Override
|
||||
public SortedSet<Integer> getSupportedVersions() {
|
||||
SortedSet<Integer> outputSet = new TreeSet<>(ProtocolRegistry.getSupportedVersions());
|
||||
outputSet.removeAll(getConf().getBlockedProtocols());
|
||||
|
||||
return outputSet;
|
||||
}
|
||||
|
||||
public boolean isShieldBlocking() {
|
||||
return getConfig().getBoolean("shield-blocking", true);
|
||||
@Override
|
||||
public boolean isSpigot() {
|
||||
return this.spigot;
|
||||
}
|
||||
|
||||
public boolean isHologramPatch() {
|
||||
return getConfig().getBoolean("hologram-patch", false);
|
||||
}
|
||||
|
||||
public boolean isBossbarPatch() {
|
||||
return getConfig().getBoolean("bossbar-patch", true);
|
||||
}
|
||||
|
||||
public boolean isBossbarAntiflicker() {
|
||||
return getConfig().getBoolean("bossbar-anti-flicker", false);
|
||||
}
|
||||
|
||||
public double getHologramYOffset() {
|
||||
return getConfig().getDouble("hologram-y", -1D);
|
||||
}
|
||||
|
||||
public boolean isAutoTeam() {
|
||||
// Collision has to be enabled first
|
||||
if (!isPreventCollision()) return false;
|
||||
return getConfig().getBoolean("auto-team", true);
|
||||
}
|
||||
|
||||
public void addPortedClient(ConnectionInfo info) {
|
||||
portedPlayers.put(info.getUUID(), info);
|
||||
public void addPortedClient(UserConnection info) {
|
||||
portedPlayers.put(info.get(ProtocolInfo.class).getUuid(), info);
|
||||
}
|
||||
|
||||
public void removePortedClient(UUID clientID) {
|
||||
@@ -307,4 +488,42 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isProtocolSupport() {
|
||||
return protocolSupport;
|
||||
}
|
||||
|
||||
public Map<UUID, UserConnection> getPortedPlayers() {
|
||||
return portedPlayers;
|
||||
}
|
||||
|
||||
public boolean handlePPS(UserConnection info) {
|
||||
// Max PPS Checker
|
||||
if (conf.getMaxPPS() > 0) {
|
||||
if (info.getPacketsPerSecond() >= conf.getMaxPPS()) {
|
||||
info.disconnect(conf.getMaxPPSKickMessage().replace("%pps", ((Long) info.getPacketsPerSecond()).intValue() + ""));
|
||||
return true; // don't send current packet
|
||||
}
|
||||
}
|
||||
|
||||
// Tracking PPS Checker
|
||||
if (conf.getMaxWarnings() > 0 && conf.getTrackingPeriod() > 0) {
|
||||
if (info.getSecondsObserved() > conf.getTrackingPeriod()) {
|
||||
// Reset
|
||||
info.setWarnings(0);
|
||||
info.setSecondsObserved(1);
|
||||
} else {
|
||||
info.setSecondsObserved(info.getSecondsObserved() + 1);
|
||||
if (info.getPacketsPerSecond() >= conf.getWarningPPS()) {
|
||||
info.setWarnings(info.getWarnings() + 1);
|
||||
}
|
||||
|
||||
if (info.getWarnings() >= conf.getMaxWarnings()) {
|
||||
info.disconnect(conf.getMaxWarningsKickMessage().replace("%pps", ((Long) info.getPacketsPerSecond()).intValue() + ""));
|
||||
return true; // don't send current packet
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||
import us.myles.ViaVersion.api.remapper.ValueCreator;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
|
||||
import us.myles.ViaVersion.packets.Direction;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
import us.myles.ViaVersion.util.PipelineUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class PacketWrapper {
|
||||
private final ByteBuf inputBuffer;
|
||||
private final UserConnection userConnection;
|
||||
private boolean send = true;
|
||||
@Setter
|
||||
@Getter
|
||||
private int id = -1;
|
||||
private LinkedList<Pair<Type, Object>> readableObjects = new LinkedList<>();
|
||||
private List<Pair<Type, Object>> packetValues = new ArrayList<>();
|
||||
|
||||
public PacketWrapper(int packetID, ByteBuf inputBuffer, UserConnection userConnection) {
|
||||
this.id = packetID;
|
||||
this.inputBuffer = inputBuffer;
|
||||
this.userConnection = userConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a part from the output
|
||||
*
|
||||
* @param type The type of the part you wish to get.
|
||||
* @param <T> The return type of the type you wish to get.
|
||||
* @param index The index of the part (relative to the type)
|
||||
* @return The requested type or throws ArrayIndexOutOfBounds
|
||||
* @throws Exception If it fails to find it, an exception will be thrown.
|
||||
*/
|
||||
public <T> T get(Type<T> type, int index) throws Exception {
|
||||
int currentIndex = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
if (packetValue.getKey() == type) { // Ref check
|
||||
if (currentIndex == index) {
|
||||
return (T) packetValue.getValue();
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a type is at an index
|
||||
*
|
||||
* @param type The type of the part you wish to get.
|
||||
* @param index The index of the part (relative to the type)
|
||||
* @return True if the type is at the index
|
||||
*/
|
||||
public boolean is(Type type, int index) {
|
||||
int currentIndex = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
if (packetValue.getKey() == type) { // Ref check
|
||||
if (currentIndex == index) {
|
||||
return true;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a type is at an index
|
||||
*
|
||||
* @param type The type of the part you wish to get.
|
||||
* @param index The index of the part (relative to the type)
|
||||
* @return True if the type is at the index
|
||||
*/
|
||||
public boolean isReadable(Type type, int index) {
|
||||
int currentIndex = 0;
|
||||
for (Pair<Type, Object> packetValue : readableObjects) {
|
||||
if (packetValue.getKey().getBaseClass() == type.getBaseClass()) { // Ref check
|
||||
if (currentIndex == index) {
|
||||
return true;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a currently existing part in the output
|
||||
*
|
||||
* @param type The type of the part you wish to set.
|
||||
* @param <T> The return type of the type you wish to set.
|
||||
* @param index The index of the part (relative to the type)
|
||||
* @param value The value of the part you wish to set it to.
|
||||
* @throws Exception If it fails to set it, an exception will be thrown.
|
||||
*/
|
||||
public <T> void set(Type<T> type, int index, T value) throws Exception {
|
||||
int currentIndex = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
if (packetValue.getKey() == type) { // Ref check
|
||||
if (currentIndex == index) {
|
||||
packetValue.setValue(value);
|
||||
return;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a type from the input.
|
||||
*
|
||||
* @param type The type you wish to read
|
||||
* @param <T> The return type of the type you wish to read.
|
||||
* @return The requested type
|
||||
* @throws Exception If it fails to read
|
||||
*/
|
||||
public <T> T read(Type<T> type) throws Exception {
|
||||
if (type == Type.NOTHING) return null;
|
||||
if (readableObjects.isEmpty()) {
|
||||
Preconditions.checkNotNull(inputBuffer, "This packet does not have an input buffer.");
|
||||
// We could in the future log input read values, but honestly for things like bulk maps, mem waste D:
|
||||
try {
|
||||
return type.read(inputBuffer);
|
||||
} catch (Exception e) {
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
} else {
|
||||
Pair<Type, Object> read = readableObjects.poll();
|
||||
if (read.getKey().equals(type) || (type.getBaseClass().equals(read.getKey().getBaseClass()) && type.getOutputClass().equals(read.getKey().getOutputClass()))) {
|
||||
return (T) read.getValue();
|
||||
} else {
|
||||
if (type == Type.NOTHING) {
|
||||
return read(type); // retry
|
||||
} else {
|
||||
Exception e = new IOException("Unable to read type " + type.getTypeName() + ", found " + read.getKey().getTypeName());
|
||||
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a type to the output.
|
||||
*
|
||||
* @param type The type to write.
|
||||
* @param <T> The return type of the type you wish to write.
|
||||
* @param value The value of the type to write.
|
||||
*/
|
||||
public <T> void write(Type<T> type, T value) {
|
||||
if (value != null) {
|
||||
if (!type.getOutputClass().isAssignableFrom(value.getClass())) {
|
||||
// attempt conversion
|
||||
if (type instanceof TypeConverter) {
|
||||
value = (T) ((TypeConverter) type).from(value);
|
||||
} else {
|
||||
System.out.println("Possible type mismatch: " + value.getClass().getName() + " -> " + type.getOutputClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
packetValues.add(new Pair<Type, Object>(type, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a value from the input and write to the output.
|
||||
*
|
||||
* @param type The type to read and write.
|
||||
* @param <T> The return type of the type you wish to pass through.
|
||||
* @return The type which was read/written.
|
||||
* @throws Exception If it failed to read or write
|
||||
*/
|
||||
public <T> T passthrough(Type<T> type) throws Exception {
|
||||
T value = read(type);
|
||||
write(type, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take all the inputs and write them to the output.
|
||||
*
|
||||
* @throws Exception If it failed to read or write
|
||||
*/
|
||||
public void passthroughAll() throws Exception {
|
||||
// Copy previous objects
|
||||
packetValues.addAll(readableObjects);
|
||||
readableObjects.clear();
|
||||
// If the buffer has readable bytes, copy them.
|
||||
if (inputBuffer.readableBytes() > 0) {
|
||||
passthrough(Type.REMAINING_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the current output to a buffer.
|
||||
*
|
||||
* @param buffer The buffer to write to.
|
||||
* @throws Exception Throws an exception if it fails to write a value.
|
||||
*/
|
||||
public void writeToBuffer(ByteBuf buffer) throws Exception {
|
||||
if (id != -1) {
|
||||
Type.VAR_INT.write(buffer, id);
|
||||
}
|
||||
if (readableObjects.size() > 0) {
|
||||
packetValues.addAll(readableObjects);
|
||||
readableObjects.clear();
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (Pair<Type, Object> packetValue : packetValues) {
|
||||
try {
|
||||
Object value = packetValue.getValue();
|
||||
if (value != null) {
|
||||
if (!packetValue.getKey().getOutputClass().isAssignableFrom(value.getClass())) {
|
||||
// attempt conversion
|
||||
if (packetValue.getKey() instanceof TypeConverter) {
|
||||
value = ((TypeConverter) packetValue.getKey()).from(value);
|
||||
} else {
|
||||
System.out.println("Possible type mismatch: " + value.getClass().getName() + " -> " + packetValue.getKey().getOutputClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
packetValue.getKey().write(buffer, value);
|
||||
} catch (Exception e) {
|
||||
throw new InformativeException(e).set("Index", index).set("Type", packetValue.getKey().getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
writeRemaining(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the input buffer / readable objects
|
||||
*/
|
||||
public void clearInputBuffer() {
|
||||
if (inputBuffer != null)
|
||||
inputBuffer.clear();
|
||||
readableObjects.clear(); // :(
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the packet, used if you have to change the packet completely
|
||||
*/
|
||||
public void clearPacket() {
|
||||
clearInputBuffer();
|
||||
packetValues.clear();
|
||||
}
|
||||
|
||||
private void writeRemaining(ByteBuf output) {
|
||||
if (inputBuffer != null) {
|
||||
output.writeBytes(inputBuffer, inputBuffer.readableBytes());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send this packet to the associated user.
|
||||
* Be careful not to send packets twice.
|
||||
* (Sends it after current)
|
||||
*
|
||||
* @param packetProtocol - The protocol version of the packet.
|
||||
* @param skipCurrentPipeline - Skip the current pipeline
|
||||
* @throws Exception if it fails to write
|
||||
*/
|
||||
public void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
|
||||
if (!isCancelled()) {
|
||||
ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline);
|
||||
user().sendRawPacket(output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the packet go through the protocol pipes and write it to ByteBuf
|
||||
*
|
||||
* @param packetProtocol - The protocol version of the packet.
|
||||
* @param skipCurrentPipeline - Skip the current pipeline
|
||||
* @return Packet buffer
|
||||
* @throws Exception if it fails to write
|
||||
*/
|
||||
private ByteBuf constructPacket(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
|
||||
// Apply current pipeline
|
||||
List<Protocol> protocols = new ArrayList<>(user().get(ProtocolInfo.class).getPipeline().pipes());
|
||||
// Other way if outgoing
|
||||
Collections.reverse(protocols);
|
||||
int index = 0;
|
||||
for (int i = 0; i < protocols.size(); i++) {
|
||||
if (protocols.get(i).getClass().equals(packetProtocol)) {
|
||||
index = skipCurrentPipeline ? (i + 1) : (i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset reader before we start
|
||||
resetReader();
|
||||
|
||||
// Apply other protocols
|
||||
apply(Direction.OUTGOING, user().get(ProtocolInfo.class).getState(), index, protocols);
|
||||
// Send
|
||||
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
|
||||
writeToBuffer(output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send this packet to the associated user.
|
||||
* Be careful not to send packets twice.
|
||||
* (Sends it after current)
|
||||
*
|
||||
* @param packetProtocol - The protocol version of the packet.
|
||||
* @throws Exception if it fails to write
|
||||
*/
|
||||
public void send(Class<? extends Protocol> packetProtocol) throws Exception {
|
||||
send(packetProtocol, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send this packet to the associated user.
|
||||
* Be careful not to send packets twice.
|
||||
* (Sends it after current)
|
||||
* Also returns the packets ChannelFuture
|
||||
*
|
||||
* @param packetProtocol - The protocol version of the packet.
|
||||
* @return The packets ChannelFuture
|
||||
* @throws Exception if it fails to write
|
||||
*/
|
||||
public ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception {
|
||||
if (!isCancelled()) {
|
||||
ByteBuf output = constructPacket(packetProtocol, true);
|
||||
return user().sendRawPacketFuture(output);
|
||||
}
|
||||
return user().getChannel().newFailedFuture(new Exception("Cancelled packet"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send this packet to the associated user.
|
||||
* Be careful not to send packets twice.
|
||||
* (Sends it after current)
|
||||
* <br />
|
||||
* <b>This method is no longer used, it's favoured to use send(Protocol) as it will handle the pipeline properly.</b>
|
||||
*
|
||||
* @throws Exception if it fails to write
|
||||
*/
|
||||
@Deprecated
|
||||
public void send() throws Exception {
|
||||
if (!isCancelled()) {
|
||||
// Send
|
||||
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
|
||||
writeToBuffer(output);
|
||||
user().sendRawPacket(output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet for the target of this packet.
|
||||
*
|
||||
* @param packetID The ID of the new packet
|
||||
* @return The newly created packet wrapper
|
||||
*/
|
||||
public PacketWrapper create(int packetID) {
|
||||
return new PacketWrapper(packetID, null, user());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet with values.
|
||||
*
|
||||
* @param packetID The ID of the new packet
|
||||
* @param init A ValueCreator to write to the packet.
|
||||
* @return The newly created packet wrapper
|
||||
* @throws Exception If it failed to write the values from the ValueCreator.
|
||||
*/
|
||||
public PacketWrapper create(int packetID, ValueCreator init) throws Exception {
|
||||
PacketWrapper wrapper = create(packetID);
|
||||
init.write(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a pipeline from an index to the wrapper
|
||||
*
|
||||
* @param direction The direction
|
||||
* @param state The state
|
||||
* @param index The index to start from
|
||||
* @param pipeline The pipeline
|
||||
* @return The current packetwrapper
|
||||
* @throws Exception
|
||||
*/
|
||||
public PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline) throws Exception {
|
||||
for (int i = index; i < pipeline.size(); i++) { // Copy to prevent from removal.
|
||||
pipeline.get(i).transform(direction, state, this);
|
||||
// Reset the reader for the packetWrapper (So it can be recycled across packets)
|
||||
resetReader();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel this packet from sending
|
||||
*/
|
||||
public void cancel() {
|
||||
this.send = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this packet is cancelled.
|
||||
*
|
||||
* @return True if the packet won't be sent.
|
||||
*/
|
||||
public boolean isCancelled() {
|
||||
return !this.send;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user associated with this Packet
|
||||
*
|
||||
* @return The user
|
||||
*/
|
||||
public UserConnection user() {
|
||||
return this.userConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the reader, so that it can be read again.
|
||||
*/
|
||||
public void resetReader() {
|
||||
// Move readable objects are packet values
|
||||
this.packetValues.addAll(readableObjects);
|
||||
this.readableObjects.clear();
|
||||
// Move all packet values to the readable for next packet.
|
||||
this.readableObjects.addAll(packetValues);
|
||||
this.packetValues.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the current packet to the server.
|
||||
* (Ensure the ID is suitable for the server version)
|
||||
*
|
||||
* @throws Exception If it failed to write
|
||||
*/
|
||||
public void sendToServer() throws Exception {
|
||||
if (!isCancelled()) {
|
||||
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
|
||||
Type.VAR_INT.write(output, ViaDecodeHandler.PASSTHROUGH_ID); // Pass through
|
||||
|
||||
writeToBuffer(output);
|
||||
|
||||
PipelineUtil.getContextBefore("decompress", user().getChannel().pipeline()).fireChannelRead(output);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PacketWrapper{" +
|
||||
"packetValues=" + packetValues +
|
||||
", readableObjects=" + readableObjects +
|
||||
", id=" + id +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class Pair<X, Y> {
|
||||
private X key;
|
||||
private Y value;
|
||||
|
||||
public Pair(X key, Y value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pair{" + key + ", " + value + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
@RequiredArgsConstructor
|
||||
public abstract class ViaListener implements Listener {
|
||||
private final ViaVersionPlugin plugin;
|
||||
private final Class<? extends Protocol> requiredPipeline;
|
||||
private boolean registered = false;
|
||||
|
||||
/**
|
||||
* Get the UserConnection from a player
|
||||
*
|
||||
* @param player Player object
|
||||
* @return The UserConnection
|
||||
*/
|
||||
protected UserConnection getUserConnection(@NonNull Player player) {
|
||||
return getUserConnection(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UserConnection from an UUID
|
||||
*
|
||||
* @param uuid UUID object
|
||||
* @return The UserConnection
|
||||
*/
|
||||
protected UserConnection getUserConnection(@NonNull UUID uuid) {
|
||||
if (!plugin.isPorted(uuid)) return null;
|
||||
return plugin.getConnection(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player is on the selected pipe
|
||||
*
|
||||
* @param player Player Object
|
||||
* @return True if on pipe
|
||||
*/
|
||||
protected boolean isOnPipe(Player player) {
|
||||
return isOnPipe(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the UUID is on the selected pipe
|
||||
*
|
||||
* @param uuid UUID Object
|
||||
* @return True if on pipe
|
||||
*/
|
||||
protected boolean isOnPipe(UUID uuid) {
|
||||
UserConnection userConnection = getUserConnection(uuid);
|
||||
return userConnection != null &&
|
||||
userConnection.get(ProtocolInfo.class).getPipeline().contains(requiredPipeline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register as Bukkit event
|
||||
*/
|
||||
public void register() {
|
||||
if (registered) return;
|
||||
|
||||
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
|
||||
public class ViaVersion {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private static ViaVersionAPI instance;
|
||||
@Getter
|
||||
private static ViaVersionConfig config;
|
||||
|
||||
public static void setInstance(ViaVersionPlugin plugin) {
|
||||
Validate.isTrue(instance == null, "Instance is already set");
|
||||
ViaVersion.instance = plugin;
|
||||
ViaVersion.config = plugin.getConf();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,10 @@ import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.api.boss.BossBar;
|
||||
import us.myles.ViaVersion.api.boss.BossColor;
|
||||
import us.myles.ViaVersion.api.boss.BossStyle;
|
||||
import us.myles.ViaVersion.api.command.ViaVersionCommand;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
|
||||
import java.util.SortedSet;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ViaVersionAPI {
|
||||
@@ -19,15 +22,25 @@ public interface ViaVersionAPI {
|
||||
|
||||
/**
|
||||
* Get protocol number from a player
|
||||
* Will also retrieve version from ProtocolSupport if it's being used.
|
||||
*
|
||||
* @param player Bukkit player object
|
||||
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
|
||||
*/
|
||||
int getPlayerVersion(Player player);
|
||||
|
||||
/**
|
||||
* Get protocol number from a player
|
||||
*
|
||||
* @param uuid UUID of a player
|
||||
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
|
||||
*/
|
||||
int getPlayerVersion(UUID uuid);
|
||||
|
||||
/**
|
||||
* Is player using 1.9?
|
||||
*
|
||||
* @param playerUUID
|
||||
* @param playerUUID UUID of a player
|
||||
* @return True if the client is on 1.9
|
||||
*/
|
||||
boolean isPorted(UUID playerUUID);
|
||||
@@ -79,9 +92,49 @@ public interface ViaVersionAPI {
|
||||
BossBar createBossBar(String title, float health, BossColor color, BossStyle style);
|
||||
|
||||
/**
|
||||
* Obtain if global debug is enabled
|
||||
* Get if global debug is enabled
|
||||
*
|
||||
* @return true if debug is enabled
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
/**
|
||||
* Get ViaVersions command handler
|
||||
*
|
||||
* @return command handler
|
||||
*/
|
||||
ViaVersionCommand getCommandHandler();
|
||||
|
||||
/**
|
||||
* Get if this version is a compatibility build for spigot.
|
||||
* Eg. 1.9.1 / 1.9.2 allow certain versions to connect
|
||||
*
|
||||
* @return True if it is
|
||||
*/
|
||||
boolean isCompatSpigotBuild();
|
||||
|
||||
/**
|
||||
* Get the supported protocol versions
|
||||
* This method removes any blocked protocol versions.
|
||||
* @see ProtocolRegistry#getSupportedVersions() for full list.
|
||||
*
|
||||
* @return a list of protocol versions
|
||||
*/
|
||||
SortedSet<Integer> getSupportedVersions();
|
||||
|
||||
/**
|
||||
* Gets if the server uses spigot
|
||||
* <p>
|
||||
* Note: Will only work after ViaVersion load
|
||||
*
|
||||
* @return True if spigot
|
||||
*/
|
||||
boolean isSpigot();
|
||||
|
||||
/**
|
||||
* Gets if protocol support is also being used.
|
||||
*
|
||||
* @return True if it is being used.
|
||||
*/
|
||||
boolean isProtocolSupport();
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package us.myles.ViaVersion.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ViaVersionConfig {
|
||||
|
||||
/**
|
||||
* Get if the plugin should check for updates
|
||||
*
|
||||
* @return true if update checking is enabled
|
||||
*/
|
||||
boolean isCheckForUpdates();
|
||||
|
||||
/**
|
||||
* Get if collision preventing for players is enabled
|
||||
*
|
||||
* @return true if collision preventing is enabled
|
||||
*/
|
||||
boolean isPreventCollision();
|
||||
|
||||
/**
|
||||
* Get if 1.9 & 1.10 clients are shown the new effect indicator in the top-right corner
|
||||
*
|
||||
* @return true if the using of the new effect indicator is enabled
|
||||
*/
|
||||
boolean isNewEffectIndicator();
|
||||
|
||||
/**
|
||||
* Get if 1.9 & 1.10 clients are shown the new death message on the death screen
|
||||
*
|
||||
* @return true if enabled
|
||||
*/
|
||||
boolean isShowNewDeathMessages();
|
||||
|
||||
/**
|
||||
* Get if metadata errors will be suppressed
|
||||
*
|
||||
* @return true if metadata errors suppression is enabled
|
||||
*/
|
||||
boolean isSuppressMetadataErrors();
|
||||
|
||||
/**
|
||||
* Get if blocking in 1.9 & 1.10 appears as a player holding a shield
|
||||
*
|
||||
* @return true if shield blocking is enabled
|
||||
*/
|
||||
boolean isShieldBlocking();
|
||||
|
||||
/**
|
||||
* Get if armor stand positions are fixed so holograms show up at the correct height in 1.9 & 1.10
|
||||
*
|
||||
* @return true if hologram patching is enabled
|
||||
*/
|
||||
boolean isHologramPatch();
|
||||
|
||||
/**
|
||||
* Get if boss bars are fixed for 1.9 & 1.10 clients
|
||||
*
|
||||
* @return true if boss bar patching is enabled
|
||||
*/
|
||||
boolean isBossbarPatch();
|
||||
|
||||
/**
|
||||
* Get if the boss bars for 1.9 & 1.10 clients are being stopped from flickering
|
||||
* This will keep all boss bars on 100% (not recommended)
|
||||
*
|
||||
* @return true if boss bar anti flickering is enabled
|
||||
*/
|
||||
boolean isBossbarAntiflicker();
|
||||
|
||||
/**
|
||||
* Get if unknown entity errors will be suppressed
|
||||
*
|
||||
* @return true if boss bar patching is enabled
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isUnknownEntitiesSuppressed();
|
||||
|
||||
/**
|
||||
* Get the vertical offset armor stands are being moved with when the hologram patch is enabled
|
||||
*
|
||||
* @return the vertical offset holograms will be moved with
|
||||
*/
|
||||
double getHologramYOffset();
|
||||
|
||||
/**
|
||||
* Get if players will be automatically put in the same team when collision preventing is enabled
|
||||
*
|
||||
* @return true if automatic teaming is enabled
|
||||
*/
|
||||
boolean isAutoTeam();
|
||||
|
||||
/**
|
||||
* Get if our block break patch is enabled to prevent weird ghost glitches.
|
||||
*
|
||||
* @return true if it is enabled.
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isBlockBreakPatch();
|
||||
|
||||
/**
|
||||
* Get the maximum number of packets a client can send per second.
|
||||
*
|
||||
* @return The number of packets a client can send per second.
|
||||
*/
|
||||
int getMaxPPS();
|
||||
|
||||
/**
|
||||
* Get the kick message sent if the user hits the max packets per second.
|
||||
*
|
||||
* @return Kick message, with colour codes using '&'
|
||||
*/
|
||||
String getMaxPPSKickMessage();
|
||||
|
||||
/**
|
||||
* The time in seconds that should be tracked for warnings
|
||||
*
|
||||
* @return Time in seconds that should be tracked for warnings
|
||||
*/
|
||||
int getTrackingPeriod();
|
||||
|
||||
/**
|
||||
* The number of packets per second to count as a warning
|
||||
*
|
||||
* @return The number of packets per second to count as a warning.
|
||||
*/
|
||||
int getWarningPPS();
|
||||
|
||||
/**
|
||||
* Get the maximum number of warnings the client can have in the interval
|
||||
*
|
||||
* @return The number of packets a client can send per second.
|
||||
*/
|
||||
int getMaxWarnings();
|
||||
|
||||
/**
|
||||
* Get the kick message sent if the user goes over the warnings in the interval
|
||||
*
|
||||
* @return Kick message, with colour codes using '&'
|
||||
*/
|
||||
String getMaxWarningsKickMessage();
|
||||
|
||||
/**
|
||||
* Is anti-xray enabled?
|
||||
*
|
||||
* @return A boolean
|
||||
*/
|
||||
boolean isAntiXRay();
|
||||
|
||||
/**
|
||||
* Send supported versions in the status response packet
|
||||
*
|
||||
* @return If true, enabled
|
||||
*/
|
||||
boolean isSendSupportedVersions();
|
||||
|
||||
/**
|
||||
* Stimulate the player tick
|
||||
*
|
||||
* @return if true, enabled
|
||||
*/
|
||||
boolean isStimulatePlayerTick();
|
||||
|
||||
/**
|
||||
* Use the item cache to prevent high resource usage
|
||||
*
|
||||
* @return if true, enabled
|
||||
*/
|
||||
boolean isItemCache();
|
||||
|
||||
/**
|
||||
* Use the NMS player ticking
|
||||
*
|
||||
* @return if true, enabled
|
||||
*/
|
||||
boolean isNMSPlayerTicking();
|
||||
|
||||
/**
|
||||
* Replace extended pistons on 1.10 chunk loading.
|
||||
*
|
||||
* @return True if to replace them
|
||||
*/
|
||||
boolean isReplacePistons();
|
||||
|
||||
/**
|
||||
* Get the id for replacing extended pistons.
|
||||
*
|
||||
* @return The integer id
|
||||
*/
|
||||
int getPistonReplacementId();
|
||||
|
||||
/**
|
||||
* Force json transform
|
||||
*
|
||||
* @return True if enabled
|
||||
*/
|
||||
boolean isForceJsonTransform();
|
||||
|
||||
/**
|
||||
* Get the blocked protocols
|
||||
*
|
||||
* @return An Integer list
|
||||
*/
|
||||
List<Integer> getBlockedProtocols();
|
||||
|
||||
/**
|
||||
* Get the custom disconnect message
|
||||
*
|
||||
* @return Disconnect message
|
||||
*/
|
||||
String getBlockedDisconnectMsg();
|
||||
|
||||
/**
|
||||
* Get the message sent to players being kicked on reload.
|
||||
* Players are kicked to stop the server crashing
|
||||
*
|
||||
* @return Disconnect message
|
||||
*/
|
||||
String getReloadDisconnectMsg();
|
||||
}
|
||||
@@ -6,13 +6,6 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BossBar {
|
||||
/**
|
||||
* Change the title
|
||||
*
|
||||
* @param title Title can be in either JSON or just text
|
||||
*/
|
||||
void setTitle(String title);
|
||||
|
||||
/**
|
||||
* Get the current title
|
||||
*
|
||||
@@ -21,11 +14,12 @@ public interface BossBar {
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Change the health
|
||||
* Change the title
|
||||
*
|
||||
* @param health this float has to be between 0F - 1F
|
||||
* @param title Title can be in either JSON or just text
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void setHealth(float health);
|
||||
BossBar setTitle(String title);
|
||||
|
||||
/**
|
||||
* Get the health
|
||||
@@ -35,25 +29,27 @@ public interface BossBar {
|
||||
float getHealth();
|
||||
|
||||
/**
|
||||
* Yay colors!
|
||||
* Change the health
|
||||
*
|
||||
* @param color Whatever color you want!
|
||||
* @param health this float has to be between 0F - 1F
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void setColor(BossColor color);
|
||||
BossBar setHealth(float health);
|
||||
|
||||
/**
|
||||
* Get the bossbar color
|
||||
*
|
||||
* @return
|
||||
* @return The colour
|
||||
*/
|
||||
BossColor getColor();
|
||||
|
||||
/**
|
||||
* Change the bosbar style
|
||||
* Yay colors!
|
||||
*
|
||||
* @param style BossStyle
|
||||
* @param color Whatever color you want!
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void setStyle(BossStyle style);
|
||||
BossBar setColor(BossColor color);
|
||||
|
||||
/**
|
||||
* Get the bosbar style
|
||||
@@ -62,37 +58,65 @@ public interface BossBar {
|
||||
*/
|
||||
BossStyle getStyle();
|
||||
|
||||
/**
|
||||
* Change the bosbar style
|
||||
*
|
||||
* @param style BossStyle
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar setStyle(BossStyle style);
|
||||
|
||||
/**
|
||||
* Show the bossbar to a player.
|
||||
*
|
||||
* @param player
|
||||
* @param player The player
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void addPlayer(Player player);
|
||||
BossBar addPlayer(Player player);
|
||||
|
||||
/**
|
||||
* Show the bossbar to a player (uuid)
|
||||
*
|
||||
* @param player uuid of the player
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar addPlayer(UUID player);
|
||||
|
||||
/**
|
||||
* add multiple players
|
||||
*
|
||||
* @param players list of players
|
||||
* @return The BossBar object
|
||||
*/
|
||||
BossBar addPlayers(Player... players);
|
||||
|
||||
/**
|
||||
* Remove the bossbar from a player
|
||||
*
|
||||
* @param player
|
||||
* @param player The player
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void removePlayer(Player player);
|
||||
BossBar removePlayer(Player player);
|
||||
|
||||
/**
|
||||
* Add flags
|
||||
*
|
||||
* @param flag
|
||||
* @param flag The flag to add
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void addFlag(BossFlag flag);
|
||||
BossBar addFlag(BossFlag flag);
|
||||
|
||||
/**
|
||||
* Remove flags.
|
||||
*
|
||||
* @param flag
|
||||
* @param flag The flag to remove
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void removeFlag(BossFlag flag);
|
||||
BossBar removeFlag(BossFlag flag);
|
||||
|
||||
/**
|
||||
* @param flag
|
||||
* @return
|
||||
* @param flag The flag to check against
|
||||
* @return True if it has the flag
|
||||
*/
|
||||
boolean hasFlag(BossFlag flag);
|
||||
|
||||
@@ -105,13 +129,17 @@ public interface BossBar {
|
||||
|
||||
/**
|
||||
* Show the bossbar to everyone (In the getPlayer set)
|
||||
*
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void show();
|
||||
BossBar show();
|
||||
|
||||
/**
|
||||
* Hide the bossbar from everyone (In the getPlayer set)
|
||||
*
|
||||
* @return The BossBar object
|
||||
*/
|
||||
void hide();
|
||||
BossBar hide();
|
||||
|
||||
/**
|
||||
* Is it visible?
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package us.myles.ViaVersion.api.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.commands.ViaCommandHandler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ViaSubCommand {
|
||||
/**
|
||||
* Subcommand name
|
||||
*
|
||||
* @return The command name
|
||||
*/
|
||||
public abstract String name();
|
||||
|
||||
/**
|
||||
* subcommand description, this'll show in /viaversion list
|
||||
*
|
||||
* @return The command description
|
||||
*/
|
||||
public abstract String description();
|
||||
|
||||
/**
|
||||
* Usage example:
|
||||
* "playerversion [name]"
|
||||
*
|
||||
* @return The command usage
|
||||
*/
|
||||
public String usage() {
|
||||
return name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission, null for everyone
|
||||
*
|
||||
* @return The permission required to use the command
|
||||
*/
|
||||
public String permission() {
|
||||
return "viaversion.admin";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets triggered on execution
|
||||
*
|
||||
* @param sender Command sender
|
||||
* @param args Arguments
|
||||
* @return command executed succesfully if false, show usage
|
||||
*/
|
||||
public abstract boolean execute(CommandSender sender, String[] args);
|
||||
|
||||
/**
|
||||
* Yay, possibility to implement tab-completion
|
||||
*
|
||||
* @param sender Command sender
|
||||
* @param args args
|
||||
* @return tab complete possibilities
|
||||
*/
|
||||
public List<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public String color(String s) {
|
||||
return ViaCommandHandler.color(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message formatted / colored
|
||||
*
|
||||
* @param sender command sender
|
||||
* @param message string message
|
||||
* @param args optional objects
|
||||
*/
|
||||
public void sendMessage(CommandSender sender, String message, Object... args) {
|
||||
ViaCommandHandler.sendMessage(sender, message, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.command;
|
||||
|
||||
public interface ViaVersionCommand {
|
||||
/**
|
||||
* Register your own subcommand inside ViaVersion
|
||||
*
|
||||
* @param command Your own SubCommand instance to handle it.
|
||||
* @throws Exception throws an exception when the subcommand already exists or if it's not valid, example: spacee
|
||||
*/
|
||||
void registerSubCommand(ViaSubCommand command) throws Exception;
|
||||
|
||||
/**
|
||||
* Check if a subcommand is registered.
|
||||
*
|
||||
* @param name Subcommand name
|
||||
* @return true if it exists
|
||||
*/
|
||||
boolean hasSubCommand(String name);
|
||||
|
||||
/**
|
||||
* Get subcommand instance by name
|
||||
*
|
||||
* @param name subcommand name
|
||||
* @return ViaSubCommand instance
|
||||
*/
|
||||
ViaSubCommand getSubCommand(String name);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package us.myles.ViaVersion.api.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class StoredObject {
|
||||
private UserConnection user;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package us.myles.ViaVersion.api.data;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Data
|
||||
public class UserConnection {
|
||||
private final SocketChannel channel;
|
||||
Map<Class, StoredObject> storedObjects = new ConcurrentHashMap<>();
|
||||
private boolean active = true;
|
||||
private boolean pendingDisconnect = false;
|
||||
private Object lastPacket;
|
||||
private long sentPackets = 0L;
|
||||
private long receivedPackets = 0L;
|
||||
// Used for tracking pps
|
||||
private long startTime = 0L;
|
||||
private long intervalPackets = 0L;
|
||||
private long packetsPerSecond = -1L;
|
||||
// Used for handling warnings (over time)
|
||||
private int secondsObserved = 0;
|
||||
private int warnings = 0;
|
||||
|
||||
|
||||
public UserConnection(SocketChannel socketChannel) {
|
||||
this.channel = socketChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the storage
|
||||
*
|
||||
* @param objectClass The class of the object to get
|
||||
* @param <T> The type of the class you want to get.
|
||||
* @return The requested object
|
||||
*/
|
||||
public <T extends StoredObject> T get(Class<T> objectClass) {
|
||||
return (T) storedObjects.get(objectClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the storage has an object
|
||||
*
|
||||
* @param objectClass The object class to check
|
||||
* @return True if the object is in the storage
|
||||
*/
|
||||
public boolean has(Class<? extends StoredObject> objectClass) {
|
||||
return storedObjects.containsKey(objectClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an object into the stored objects based on class
|
||||
*
|
||||
* @param object The object to store.
|
||||
*/
|
||||
public void put(StoredObject object) {
|
||||
storedObjects.put(object.getClass(), object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a raw packet to the player
|
||||
*
|
||||
* @param packet The raw packet to send
|
||||
* @param currentThread Should it run in the same thread
|
||||
*/
|
||||
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
|
||||
final ChannelHandler handler = channel.pipeline().get("encoder");
|
||||
if (currentThread) {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
} else {
|
||||
channel.eventLoop().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a raw packet to the player with returning the future
|
||||
*
|
||||
* @param packet The raw packet to send
|
||||
* @return ChannelFuture of the packet being sent
|
||||
*/
|
||||
public ChannelFuture sendRawPacketFuture(final ByteBuf packet) {
|
||||
final ChannelHandler handler = channel.pipeline().get("encoder");
|
||||
return channel.pipeline().context(handler).writeAndFlush(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a raw packet to the player (netty thread)
|
||||
*
|
||||
* @param packet The packet to send
|
||||
*/
|
||||
public void sendRawPacket(final ByteBuf packet) {
|
||||
sendRawPacket(packet, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for incrementing the number of packets sent to the client
|
||||
*/
|
||||
public void incrementSent() {
|
||||
this.sentPackets++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for incrementing the number of packets received from the client
|
||||
*
|
||||
* @return True if the interval has reset
|
||||
*/
|
||||
public boolean incrementReceived() {
|
||||
// handle stats
|
||||
Long diff = System.currentTimeMillis() - startTime;
|
||||
if (diff >= 1000) {
|
||||
packetsPerSecond = intervalPackets;
|
||||
startTime = System.currentTimeMillis();
|
||||
intervalPackets = 1;
|
||||
return true;
|
||||
} else {
|
||||
intervalPackets++;
|
||||
}
|
||||
// increase total
|
||||
this.receivedPackets++;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect a connection
|
||||
*
|
||||
* @param reason The reason to use, not used if player is not active.
|
||||
*/
|
||||
public void disconnect(final String reason) {
|
||||
if (!getChannel().isOpen()) return;
|
||||
if (pendingDisconnect) return;
|
||||
pendingDisconnect = true;
|
||||
if (get(ProtocolInfo.class).getUuid() != null) {
|
||||
final UUID uuid = get(ProtocolInfo.class).getUuid();
|
||||
if (Bukkit.getPlayer(uuid) != null) {
|
||||
Bukkit.getScheduler().runTask(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if (player != null)
|
||||
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
getChannel().close(); // =)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package us.myles.ViaVersion.api.minecraft;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class Position {
|
||||
private Long x;
|
||||
private Long y;
|
||||
private Long z;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||
|
||||
public interface Chunk {
|
||||
int getX();
|
||||
|
||||
int getZ();
|
||||
|
||||
ChunkSection[] getSections();
|
||||
|
||||
boolean isGroundUp();
|
||||
|
||||
boolean isBiomeData();
|
||||
|
||||
byte[] getBiomeData();
|
||||
|
||||
int getBitmask();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface ChunkSection {
|
||||
int getBlockId(int x, int y, int z);
|
||||
|
||||
void writeBlocks(ByteBuf output) throws Exception;
|
||||
|
||||
void writeBlockLight(ByteBuf output) throws Exception;
|
||||
|
||||
boolean hasSkyLight();
|
||||
|
||||
void writeSkyLight(ByteBuf output) throws Exception;
|
||||
}
|
||||
+63
-10
@@ -1,4 +1,4 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -6,7 +6,7 @@ public class NibbleArray {
|
||||
private final byte[] handle;
|
||||
|
||||
public NibbleArray(int length) {
|
||||
if(length == 0 || length % 2 != 0) {
|
||||
if (length == 0 || length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
|
||||
}
|
||||
|
||||
@@ -14,61 +14,114 @@ public class NibbleArray {
|
||||
}
|
||||
|
||||
public NibbleArray(byte[] handle) {
|
||||
if(handle.length == 0 || handle.length % 2 != 0) {
|
||||
if (handle.length == 0 || handle.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
|
||||
}
|
||||
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value at a desired X, Y, Z
|
||||
*
|
||||
* @param x Block X
|
||||
* @param y Block Y
|
||||
* @param z Block Z
|
||||
* @return The value at the given XYZ
|
||||
*/
|
||||
public byte get(int x, int y, int z) {
|
||||
return get(y << 8 | z << 4 | x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value at an index
|
||||
*
|
||||
* @param index The index to lookup
|
||||
* @return The value at that index.
|
||||
*/
|
||||
public byte get(int index) {
|
||||
byte value = handle[index / 2];
|
||||
if(index % 2 == 0) {
|
||||
if (index % 2 == 0) {
|
||||
return (byte) (value & 0xF);
|
||||
} else {
|
||||
return (byte) ((value >> 4) & 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value based on an x, y, z
|
||||
*
|
||||
* @param x Block X
|
||||
* @param y Block Y
|
||||
* @param z Block Z
|
||||
* @param value Desired Value
|
||||
*/
|
||||
public void set(int x, int y, int z, int value) {
|
||||
set(y << 8 | z << 4 | x, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value at an index
|
||||
*
|
||||
* @param index The index to set the value at.
|
||||
* @param value The desired value
|
||||
*/
|
||||
public void set(int index, int value) {
|
||||
index /= 2;
|
||||
if(index % 2 == 0) {
|
||||
if (index % 2 == 0) {
|
||||
handle[index] = (byte) (handle[index] & 0xF0 | value & 0xF);
|
||||
} else {
|
||||
handle[index] = (byte) (handle[index] & 0xF | (value & 0xF) << 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of this nibble
|
||||
*
|
||||
* @return The size as an int of the nibble
|
||||
*/
|
||||
public int size() {
|
||||
return handle.length * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual number of bytes
|
||||
*
|
||||
* @return The number of bytes based on the handle.
|
||||
*/
|
||||
public int actualSize() {
|
||||
return handle.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the array with a value
|
||||
*
|
||||
* @param value Value to fill with
|
||||
*/
|
||||
public void fill(byte value) {
|
||||
value &= 0xF; // Max nibble size (= 16)
|
||||
Arrays.fill(handle, (byte) ((value << 4) | value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the byte array behind this nibble
|
||||
*
|
||||
* @return The byte array
|
||||
*/
|
||||
public byte[] getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a byte array into this nibble
|
||||
*
|
||||
* @param handle The byte array to copy in.
|
||||
*/
|
||||
public void setHandle(byte[] handle) {
|
||||
if(handle.length != this.handle.length) {
|
||||
if (handle.length != this.handle.length) {
|
||||
throw new IllegalArgumentException("Length of handle must equal to size of nibble array!");
|
||||
}
|
||||
|
||||
System.arraycopy(handle, 0, this.handle, 0, handle.length);
|
||||
}
|
||||
|
||||
public byte[] getHandle() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package us.myles.ViaVersion.api.minecraft.item;
|
||||
|
||||
import lombok.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class Item {
|
||||
private short id;
|
||||
private byte amount;
|
||||
private short data;
|
||||
private CompoundTag tag;
|
||||
|
||||
/**
|
||||
* Create an item from a bukkit stack (doesn't save NBT)
|
||||
*
|
||||
* @param stack The stack to convert from
|
||||
* @return The output stack
|
||||
*/
|
||||
public static Item getItem(ItemStack stack) {
|
||||
if (stack == null) return null;
|
||||
return new Item((short) stack.getTypeId(), (byte) stack.getAmount(), stack.getDurability(), null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package us.myles.ViaVersion.api.minecraft.metadata;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class Metadata {
|
||||
private int id;
|
||||
private int typeID;
|
||||
private Type type;
|
||||
private Object value;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Metadata{" +
|
||||
"id=" + id +
|
||||
", typeID=" + typeID +
|
||||
", type=" + type +
|
||||
", value=" + value +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.remapper.PacketRemapper;
|
||||
import us.myles.ViaVersion.exception.CancelException;
|
||||
import us.myles.ViaVersion.packets.Direction;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class Protocol {
|
||||
private Map<Pair<State, Integer>, ProtocolPacket> incoming = new HashMap<>();
|
||||
private Map<Pair<State, Integer>, ProtocolPacket> outgoing = new HashMap<>();
|
||||
|
||||
public Protocol() {
|
||||
registerPackets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this protocol filter an object packet from this class.
|
||||
* Default: false
|
||||
*
|
||||
* @param packetClass The class of the current input
|
||||
* @return True if it should handle the filtering
|
||||
*/
|
||||
public boolean isFiltered(Class packetClass) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a packet into the output
|
||||
*
|
||||
* @param info The current user connection
|
||||
* @param packet The input packet as an object (NMS)
|
||||
* @param output The list to put the object into.
|
||||
* @throws Exception Throws exception if cancelled / error.
|
||||
*/
|
||||
protected void filterPacket(UserConnection info, Object packet, List output) throws Exception {
|
||||
output.add(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register listeners for this protocol
|
||||
*/
|
||||
protected void registerListeners() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the packets for this protocol
|
||||
*/
|
||||
protected abstract void registerPackets();
|
||||
|
||||
/**
|
||||
* Initialise a user for this protocol setting up objects.
|
||||
*
|
||||
* @param userConnection The user to initialise
|
||||
*/
|
||||
public abstract void init(UserConnection userConnection);
|
||||
|
||||
/**
|
||||
* Register an incoming packet, with simple id transformation.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
*/
|
||||
public void registerIncoming(State state, int oldPacketID, int newPacketID) {
|
||||
registerIncoming(state, oldPacketID, newPacketID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an incoming packet, with id transformation and remapper.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
* @param packetRemapper The remapper to use for the packet
|
||||
*/
|
||||
public void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
|
||||
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
|
||||
incoming.put(new Pair<>(state, newPacketID), protocolPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an outgoing packet, with simple id transformation.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
*/
|
||||
public void registerOutgoing(State state, int oldPacketID, int newPacketID) {
|
||||
registerOutgoing(state, oldPacketID, newPacketID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an outgoing packet, with id transformation and remapper.
|
||||
*
|
||||
* @param state The state which the packet is sent in.
|
||||
* @param oldPacketID The old packet ID
|
||||
* @param newPacketID The new packet ID
|
||||
* @param packetRemapper The remapper to use for the packet
|
||||
*/
|
||||
public void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
|
||||
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
|
||||
outgoing.put(new Pair<>(state, oldPacketID), protocolPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a packet using this protocol
|
||||
*
|
||||
* @param direction The direction the packet is going in
|
||||
* @param state The current protocol state
|
||||
* @param packetWrapper The packet wrapper to transform
|
||||
* @throws Exception Throws exception if it fails to transform
|
||||
*/
|
||||
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
|
||||
Pair<State, Integer> statePacket = new Pair<>(state, packetWrapper.getId());
|
||||
Map<Pair<State, Integer>, ProtocolPacket> packetMap = (direction == Direction.OUTGOING ? outgoing : incoming);
|
||||
ProtocolPacket protocolPacket;
|
||||
if (packetMap.containsKey(statePacket)) {
|
||||
protocolPacket = packetMap.get(statePacket);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// write packet id
|
||||
int newID = direction == Direction.OUTGOING ? protocolPacket.getNewID() : protocolPacket.getOldID();
|
||||
packetWrapper.setId(newID);
|
||||
// remap
|
||||
if (protocolPacket.getRemapper() != null) {
|
||||
protocolPacket.getRemapper().remap(packetWrapper);
|
||||
if (packetWrapper.isCancelled())
|
||||
throw new CancelException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Protocol:" + getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
class ProtocolPacket {
|
||||
State state;
|
||||
int oldID;
|
||||
int newID;
|
||||
PacketRemapper remapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.packets.Direction;
|
||||
import us.myles.ViaVersion.packets.PacketType;
|
||||
import us.myles.ViaVersion.packets.State;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ProtocolPipeline extends Protocol {
|
||||
List<Protocol> protocolList;
|
||||
private UserConnection userConnection;
|
||||
|
||||
public ProtocolPipeline(UserConnection userConnection) {
|
||||
super();
|
||||
init(userConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerPackets() {
|
||||
protocolList = new CopyOnWriteArrayList<>();
|
||||
// This is a pipeline so we register basic pipes
|
||||
protocolList.add(ProtocolRegistry.BASE_PROTOCOL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(UserConnection userConnection) {
|
||||
this.userConnection = userConnection;
|
||||
|
||||
ProtocolInfo protocolInfo = new ProtocolInfo(userConnection);
|
||||
protocolInfo.setPipeline(this);
|
||||
|
||||
userConnection.put(protocolInfo);
|
||||
|
||||
/* Init through all our pipes */
|
||||
for (Protocol protocol : protocolList) {
|
||||
protocol.init(userConnection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a protocol to the current pipeline
|
||||
* This will call the .init method.
|
||||
*
|
||||
* @param protocol The protocol to add to the end
|
||||
*/
|
||||
public void add(Protocol protocol) {
|
||||
if (protocolList != null) {
|
||||
protocolList.add(protocol);
|
||||
protocol.init(userConnection);
|
||||
} else {
|
||||
throw new NullPointerException("Tried to add protocol to early");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
|
||||
int originalID = packetWrapper.getId();
|
||||
List<Protocol> protocols = new ArrayList<>(protocolList);
|
||||
// Other way if outgoing
|
||||
if (direction == Direction.OUTGOING)
|
||||
Collections.reverse(protocols);
|
||||
|
||||
// Apply protocols
|
||||
packetWrapper.apply(direction, state, 0, protocols);
|
||||
|
||||
super.transform(direction, state, packetWrapper);
|
||||
|
||||
if (ViaVersion.getInstance().isDebug()) {
|
||||
// Debug packet
|
||||
String packet = "UNKNOWN";
|
||||
|
||||
// For 1.8/1.9 server version, eventually we'll probably get an API for this...
|
||||
if (ProtocolRegistry.SERVER_PROTOCOL >= ProtocolVersion.v1_8.getId() &&
|
||||
ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_9_3.getId()) {
|
||||
|
||||
PacketType type;
|
||||
if (ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_8.getId()) {
|
||||
if (direction == Direction.INCOMING) {
|
||||
type = PacketType.findNewPacket(state, direction, originalID);
|
||||
} else {
|
||||
type = PacketType.findOldPacket(state, direction, originalID);
|
||||
}
|
||||
} else {
|
||||
type = PacketType.findNewPacket(state, direction, originalID);
|
||||
}
|
||||
if (type != null) {
|
||||
// Filter :) This would be not hard coded too, sorry :(
|
||||
if (type == PacketType.PLAY_CHUNK_DATA) return;
|
||||
if (type == PacketType.PLAY_TIME_UPDATE) return;
|
||||
if (type == PacketType.PLAY_KEEP_ALIVE) return;
|
||||
if (type == PacketType.PLAY_KEEP_ALIVE_REQUEST) return;
|
||||
if (type == PacketType.PLAY_ENTITY_LOOK_MOVE) return;
|
||||
if (type == PacketType.PLAY_ENTITY_LOOK) return;
|
||||
if (type == PacketType.PLAY_ENTITY_RELATIVE_MOVE) return;
|
||||
if (type == PacketType.PLAY_PLAYER_POSITION_LOOK_REQUEST) return;
|
||||
if (type == PacketType.PLAY_PLAYER_LOOK_REQUEST) return;
|
||||
if (type == PacketType.PLAY_PLAYER_POSITION_REQUEST) return;
|
||||
|
||||
packet = type.name();
|
||||
}
|
||||
}
|
||||
String name = packet + "[" + userConnection.get(ProtocolInfo.class).getProtocolVersion() + "]";
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
String actualUsername = packetWrapper.user().get(ProtocolInfo.class).getUsername();
|
||||
String username = actualUsername != null ? actualUsername + " " : "";
|
||||
|
||||
plugin.getLogger().log(Level.INFO, "{0}{1}: {2} {3} -> {4} [{5}] Value: {6}",
|
||||
new Object[]{
|
||||
username,
|
||||
direction,
|
||||
state,
|
||||
originalID,
|
||||
packetWrapper.getId(),
|
||||
name,
|
||||
packetWrapper
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the pipeline contains a protocol
|
||||
*
|
||||
* @param pipeClass The class to check
|
||||
* @return True if the protocol class is in the pipeline
|
||||
*/
|
||||
public boolean contains(Class<? extends Protocol> pipeClass) {
|
||||
for (Protocol protocol : protocolList) {
|
||||
if (protocol.getClass().equals(pipeClass)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the pipeline to filter a NMS packet
|
||||
*
|
||||
* @param o The NMS packet object
|
||||
* @param list The output list to write to
|
||||
* @return If it should not write the input object to te list.
|
||||
* @throws Exception If it failed to convert / packet cancelld.
|
||||
*/
|
||||
public boolean filter(Object o, List list) throws Exception {
|
||||
for (Protocol protocol : protocolList) {
|
||||
if (protocol.isFiltered(o.getClass())) {
|
||||
protocol.filterPacket(userConnection, o, list);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Protocol> pipes() {
|
||||
return protocolList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.bukkit.Bukkit;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.protocols.base.BaseProtocol;
|
||||
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.Protocol1_9_1_2TO1_9_3_4;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9_1to1_9.Protocol1_9_1TO1_9;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.Protocol1_9_3TO1_9_1_2;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_9_1.Protocol1_9TO1_9_1;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ProtocolRegistry {
|
||||
public static final Protocol BASE_PROTOCOL = new BaseProtocol();
|
||||
public static int SERVER_PROTOCOL = -1;
|
||||
// Input Version -> Output Version & Protocol (Allows fast lookup)
|
||||
private static Map<Integer, Map<Integer, Protocol>> registryMap = new ConcurrentHashMap<>();
|
||||
private static Map<Pair<Integer, Integer>, List<Pair<Integer, Protocol>>> pathCache = new ConcurrentHashMap<>();
|
||||
private static List<Protocol> registerList = Lists.newCopyOnWriteArrayList();
|
||||
private static Set<Integer> supportedVersions = Sets.newConcurrentHashSet();
|
||||
|
||||
static {
|
||||
// Base Protocol
|
||||
registerProtocol(BASE_PROTOCOL, Arrays.<Integer>asList(), -1);
|
||||
// Register built in protocols
|
||||
registerProtocol(new Protocol1_9TO1_8(), Collections.singletonList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_8.getId());
|
||||
registerProtocol(new Protocol1_9_1TO1_9(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9.getId());
|
||||
registerProtocol(new Protocol1_9_3TO1_9_1_2(), Arrays.asList(ProtocolVersion.v1_9_3.getId()), ProtocolVersion.v1_9_2.getId());
|
||||
// Only supported for 1.9.4 server to 1.9 (nothing else)
|
||||
registerProtocol(new Protocol1_9TO1_9_1(), Arrays.asList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_9_2.getId());
|
||||
registerProtocol(new Protocol1_9_1_2TO1_9_3_4(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9_3.getId());
|
||||
registerProtocol(new Protocol1_10To1_9_3_4(), Collections.singletonList(ProtocolVersion.v1_10.getId()), ProtocolVersion.v1_9_3.getId());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a protocol
|
||||
*
|
||||
* @param protocol The protocol to register.
|
||||
* @param supported Supported client versions.
|
||||
* @param output The output server version it converts to.
|
||||
*/
|
||||
public static void registerProtocol(Protocol protocol, List<Integer> supported, Integer output) {
|
||||
// Clear cache as this may make new routes.
|
||||
if (pathCache.size() > 0)
|
||||
pathCache.clear();
|
||||
|
||||
for (Integer version : supported) {
|
||||
if (!registryMap.containsKey(version)) {
|
||||
registryMap.put(version, new HashMap<Integer, Protocol>());
|
||||
}
|
||||
|
||||
registryMap.get(version).put(output, protocol);
|
||||
}
|
||||
|
||||
if (Bukkit.getPluginManager().getPlugin("ViaVersion").isEnabled()) {
|
||||
protocol.registerListeners();
|
||||
refreshVersions();
|
||||
} else {
|
||||
registerList.add(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public static void refreshVersions() {
|
||||
supportedVersions.clear();
|
||||
|
||||
supportedVersions.add(ProtocolRegistry.SERVER_PROTOCOL);
|
||||
for (ProtocolVersion versions : ProtocolVersion.getProtocols()) {
|
||||
List<Pair<Integer, Protocol>> paths = getProtocolPath(versions.getId(), ProtocolRegistry.SERVER_PROTOCOL);
|
||||
if (paths == null) continue;
|
||||
supportedVersions.add(versions.getId());
|
||||
for (Pair<Integer, Protocol> path : paths)
|
||||
supportedVersions.add(path.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the versions compatible with the server.
|
||||
*
|
||||
* @return Read-only set of the versions.
|
||||
*/
|
||||
public static SortedSet<Integer> getSupportedVersions() {
|
||||
return Collections.unmodifiableSortedSet(new TreeSet<>(supportedVersions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this plugin is useful to the server.
|
||||
*
|
||||
* @return True if there is a useful pipe
|
||||
*/
|
||||
public static boolean isWorkingPipe() {
|
||||
for (Map<Integer, Protocol> maps : registryMap.values()) {
|
||||
if (maps.containsKey(SERVER_PROTOCOL)) return true;
|
||||
}
|
||||
return false; // No destination for protocol
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server is enabled, to register any non registered listeners.
|
||||
*/
|
||||
public static void registerListeners() {
|
||||
for (Protocol protocol : registerList) {
|
||||
protocol.registerListeners();
|
||||
}
|
||||
registerList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a path to get from an input protocol to the servers protocol.
|
||||
*
|
||||
* @param current The current items in the path
|
||||
* @param clientVersion The current input version
|
||||
* @param serverVersion The desired output version
|
||||
* @return The path which has been generated, null if failed.
|
||||
*/
|
||||
private static List<Pair<Integer, Protocol>> getProtocolPath(List<Pair<Integer, Protocol>> current, int clientVersion, int serverVersion) {
|
||||
if (clientVersion == serverVersion) return null; // We're already there
|
||||
if (current.size() > 50) return null; // Fail safe, protocol too complicated.
|
||||
|
||||
// First check if there is any protocols for this
|
||||
if (!registryMap.containsKey(clientVersion)) {
|
||||
return null; // Not supported
|
||||
}
|
||||
// Next check there isn't an obvious path
|
||||
Map<Integer, Protocol> inputMap = registryMap.get(clientVersion);
|
||||
if (inputMap.containsKey(serverVersion)) {
|
||||
current.add(new Pair<>(serverVersion, inputMap.get(serverVersion)));
|
||||
return current; // Easy solution
|
||||
}
|
||||
// There might be a more advanced solution... So we'll see if any of the others can get us there
|
||||
List<Pair<Integer, Protocol>> shortest = null;
|
||||
|
||||
for (Map.Entry<Integer, Protocol> entry : inputMap.entrySet()) {
|
||||
// Ensure it wasn't caught by the other loop
|
||||
if (!entry.getKey().equals(serverVersion)) {
|
||||
Pair<Integer, Protocol> pair = new Pair<>(entry.getKey(), entry.getValue());
|
||||
// Ensure no recursion
|
||||
if (!current.contains(pair)) {
|
||||
// Create a copy
|
||||
List<Pair<Integer, Protocol>> newCurrent = new ArrayList<>(current);
|
||||
newCurrent.add(pair);
|
||||
// Calculate the rest of the protocol using the current
|
||||
newCurrent = getProtocolPath(newCurrent, entry.getKey(), serverVersion);
|
||||
if (newCurrent != null) {
|
||||
// If it's shorter then choose it
|
||||
if (shortest == null || shortest.size() > newCurrent.size()) {
|
||||
shortest = newCurrent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shortest; // null if none found
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a path from a client version to server version
|
||||
*
|
||||
* @param clientVersion The input client version
|
||||
* @param serverVersion The desired output server version
|
||||
* @return The path it generated, null if it failed.
|
||||
*/
|
||||
public static List<Pair<Integer, Protocol>> getProtocolPath(int clientVersion, int serverVersion) {
|
||||
Pair<Integer, Integer> protocolKey = new Pair<>(clientVersion, serverVersion);
|
||||
// Check cache
|
||||
if (pathCache.containsKey(protocolKey)) {
|
||||
return pathCache.get(protocolKey);
|
||||
}
|
||||
// Generate path
|
||||
List<Pair<Integer, Protocol>> outputPath = getProtocolPath(new ArrayList<Pair<Integer, Protocol>>(), clientVersion, serverVersion);
|
||||
// If it found a path, cache it.
|
||||
if (outputPath != null) {
|
||||
pathCache.put(protocolKey, outputPath);
|
||||
}
|
||||
return outputPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package us.myles.ViaVersion.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class ProtocolVersion {
|
||||
private static final Map<Integer, ProtocolVersion> versions = new HashMap<>();
|
||||
private static final List<ProtocolVersion> versionList = new ArrayList<>();
|
||||
|
||||
public static final ProtocolVersion v1_4_6;
|
||||
public static final ProtocolVersion v1_5_1;
|
||||
public static final ProtocolVersion v1_5_2;
|
||||
public static final ProtocolVersion v_1_6_1;
|
||||
public static final ProtocolVersion v_1_6_2;
|
||||
public static final ProtocolVersion v_1_6_3;
|
||||
public static final ProtocolVersion v_1_6_4;
|
||||
public static final ProtocolVersion v1_7_1;
|
||||
public static final ProtocolVersion v1_7_6;
|
||||
public static final ProtocolVersion v1_8;
|
||||
public static final ProtocolVersion v1_9;
|
||||
public static final ProtocolVersion v1_9_1;
|
||||
public static final ProtocolVersion v1_9_2;
|
||||
public static final ProtocolVersion v1_9_3;
|
||||
public static final ProtocolVersion v1_10;
|
||||
public static final ProtocolVersion unknown;
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
|
||||
static {
|
||||
// Before netty rewrite
|
||||
register(v1_4_6 = new ProtocolVersion(51, "1.4.6"));
|
||||
register(v1_5_1 = new ProtocolVersion(60, "1.5.1"));
|
||||
register(v1_5_2 = new ProtocolVersion(61, "1.5.2"));
|
||||
register(v_1_6_1 = new ProtocolVersion(73, "1.6.1"));
|
||||
register(v_1_6_2 = new ProtocolVersion(74, "1.6.2"));
|
||||
register(v_1_6_3 = new ProtocolVersion(77, "1.6.3"));
|
||||
register(v_1_6_4 = new ProtocolVersion(78, "1.6.4"));
|
||||
// After netty rewrite
|
||||
register(v1_7_1 = new ProtocolVersion(4, "1.7-1.7.5"));
|
||||
register(v1_7_6 = new ProtocolVersion(5, "1.7.6-1.7.10"));
|
||||
register(v1_8 = new ProtocolVersion(47, "1.8.x"));
|
||||
register(v1_9 = new ProtocolVersion(107, "1.9"));
|
||||
register(v1_9_1 = new ProtocolVersion(108, "1.9.1"));
|
||||
register(v1_9_2 = new ProtocolVersion(109, "1.9.2"));
|
||||
register(v1_9_3 = new ProtocolVersion(110, "1.9.3/4"));
|
||||
register(v1_10 = new ProtocolVersion(210, "1.10"));
|
||||
register(unknown = new ProtocolVersion(-1, "UNKNOWN"));
|
||||
}
|
||||
|
||||
public static void register(@NonNull ProtocolVersion protocol) {
|
||||
versions.put(protocol.getId(), protocol);
|
||||
versionList.add(protocol);
|
||||
}
|
||||
|
||||
public static boolean isRegistered(int id) {
|
||||
return versions.containsKey(id);
|
||||
}
|
||||
|
||||
public static ProtocolVersion getProtocol(int id) {
|
||||
if (versions.containsKey(id)) {
|
||||
return versions.get(id);
|
||||
} else {
|
||||
return new ProtocolVersion(id, "Unknown (" + id + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public static int getIndex(ProtocolVersion version) {
|
||||
return versionList.indexOf(version);
|
||||
}
|
||||
|
||||
public static List<ProtocolVersion> getProtocols() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(versions.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ProtocolVersion that = (ProtocolVersion) o;
|
||||
return id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
public abstract class PacketHandler implements ValueWriter {
|
||||
/**
|
||||
* Handle a packet
|
||||
*
|
||||
* @param wrapper The associated wrapper
|
||||
* @throws Exception Throws exception if it failed to handle the packet
|
||||
*/
|
||||
public abstract void handle(PacketWrapper wrapper) throws Exception;
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper writer, Object inputValue) throws Exception {
|
||||
try {
|
||||
handle(writer);
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PacketRemapper {
|
||||
private List<Pair<ValueReader, ValueWriter>> valueRemappers = new ArrayList<>();
|
||||
|
||||
public PacketRemapper() {
|
||||
registerMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type to the same type.
|
||||
*
|
||||
* @param type Type to map
|
||||
*/
|
||||
public void map(Type type) {
|
||||
TypeRemapper remapper = new TypeRemapper(type);
|
||||
map(remapper, remapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type from an old type to a new type
|
||||
*
|
||||
* @param oldType The old type
|
||||
* @param newType The new type
|
||||
*/
|
||||
public void map(Type oldType, Type newType) {
|
||||
map(new TypeRemapper(oldType), new TypeRemapper(newType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type from an old type to a transformed new type.
|
||||
*
|
||||
* @param oldType The old type
|
||||
* @param <T1> The old return type.
|
||||
* @param transformer The transformer to use to produce the new type.
|
||||
* @param <T2> The new return type.
|
||||
*/
|
||||
public <T1, T2> void map(Type<T1> oldType, ValueTransformer<T1, T2> transformer) {
|
||||
map(new TypeRemapper(oldType), transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a type using a basic ValueReader to a ValueWriter
|
||||
*
|
||||
* @param inputReader The reader to read with.
|
||||
* @param outputWriter The writer to write with
|
||||
* @param <T> The return type
|
||||
*/
|
||||
public <T> void map(ValueReader<T> inputReader, ValueWriter<T> outputWriter) {
|
||||
valueRemappers.add(new Pair<ValueReader, ValueWriter>(inputReader, outputWriter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a value
|
||||
*
|
||||
* @param creator The creator to used to make the value(s).
|
||||
*/
|
||||
public void create(ValueCreator creator) {
|
||||
map(new TypeRemapper(Type.NOTHING), creator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler
|
||||
*
|
||||
* @param handler The handler to use to handle the current packet.
|
||||
*/
|
||||
public void handler(PacketHandler handler) {
|
||||
map(new TypeRemapper(Type.NOTHING), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the mappings for this packet
|
||||
*/
|
||||
public abstract void registerMap();
|
||||
|
||||
/**
|
||||
* Remap a packet wrapper
|
||||
*
|
||||
* @param packetWrapper The wrapper to remap
|
||||
* @throws Exception Throws if it fails to write / read to the packet.
|
||||
*/
|
||||
public void remap(PacketWrapper packetWrapper) throws Exception {
|
||||
try {
|
||||
// Read all the current values
|
||||
for (Pair<ValueReader, ValueWriter> valueRemapper : valueRemappers) {
|
||||
Object object = valueRemapper.getKey().read(packetWrapper);
|
||||
// Convert object to write type :O!!!
|
||||
valueRemapper.getValue().write(packetWrapper, object);
|
||||
}
|
||||
// If we had handlers we'd put them here
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class TypeRemapper<T> implements ValueReader<T>, ValueWriter<T> {
|
||||
private final Type<T> type;
|
||||
|
||||
public TypeRemapper(Type<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(PacketWrapper wrapper) throws Exception {
|
||||
return wrapper.read(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper output, T inputValue) {
|
||||
output.write(type, inputValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
public abstract class ValueCreator implements ValueWriter {
|
||||
/**
|
||||
* Write new values to a Packet.
|
||||
*
|
||||
* @param wrapper The packet to write to
|
||||
* @throws Exception Throws exception if it fails to write.
|
||||
*/
|
||||
public abstract void write(PacketWrapper wrapper) throws Exception;
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper writer, Object inputValue) throws Exception {
|
||||
try {
|
||||
write(writer);
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
|
||||
public interface ValueReader<T> {
|
||||
/**
|
||||
* Reads value from a PacketWrapper
|
||||
*
|
||||
* @param wrapper The wrapper to read from
|
||||
* @return Returns the desired type
|
||||
* @throws Exception Throws exception if it fails to read
|
||||
*/
|
||||
T read(PacketWrapper wrapper) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.exception.InformativeException;
|
||||
|
||||
public abstract class ValueTransformer<T1, T2> implements ValueWriter<T1> {
|
||||
private final Type<T2> outputType;
|
||||
|
||||
public ValueTransformer(Type<T2> outputType) {
|
||||
this.outputType = outputType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a value from one type to another
|
||||
*
|
||||
* @param wrapper The current packet
|
||||
* @param inputValue The input value
|
||||
* @return The value to write to the wrapper
|
||||
* @throws Exception Throws exception if it fails to transform a value
|
||||
*/
|
||||
public abstract T2 transform(PacketWrapper wrapper, T1 inputValue) throws Exception;
|
||||
|
||||
@Override
|
||||
public void write(PacketWrapper writer, T1 inputValue) throws Exception {
|
||||
try {
|
||||
writer.write(outputType, transform(writer, inputValue));
|
||||
} catch (InformativeException e) {
|
||||
e.addSource(this.getClass());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.remapper;
|
||||
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
|
||||
public interface ValueWriter<T> {
|
||||
/**
|
||||
* Write a value to a packet
|
||||
*
|
||||
* @param writer The packet wrapper to write to
|
||||
* @param inputValue The value to write
|
||||
* @throws Exception Throws exception if it fails to write
|
||||
*/
|
||||
void write(PacketWrapper writer, T inputValue) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface ByteBufReader<T> {
|
||||
/**
|
||||
* Read a value from a ByteBuf
|
||||
*
|
||||
* @param buffer The buffer to read from.
|
||||
* @return The type based on the class type.
|
||||
* @throws Exception Throws exception if it failed reading.
|
||||
*/
|
||||
T read(ByteBuf buffer) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface ByteBufWriter<T> {
|
||||
/**
|
||||
* Write an object to a type to a ByteBuf
|
||||
*
|
||||
* @param buffer The buffer to write to
|
||||
* @param object The object to write
|
||||
* @throws Exception Throws if it failed to write
|
||||
*/
|
||||
void write(ByteBuf buffer, T object) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public abstract class PartialType<T, X> extends Type<T> {
|
||||
private final X param;
|
||||
|
||||
public PartialType(X param, Class<T> type) {
|
||||
super(type);
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public abstract T read(ByteBuf buffer, X param) throws Exception;
|
||||
|
||||
public abstract void write(ByteBuf buffer, X param, T object) throws Exception;
|
||||
|
||||
@Override
|
||||
public T read(ByteBuf buffer) throws Exception {
|
||||
return read(buffer, this.param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, T object) throws Exception {
|
||||
write(buffer, this.param, object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.util.EulerAngle;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.minecraft.item.Item;
|
||||
import us.myles.ViaVersion.api.type.types.*;
|
||||
import us.myles.ViaVersion.api.type.types.minecraft.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
|
||||
/* Defined Types */
|
||||
public static final Type<Byte> BYTE = new ByteType();
|
||||
public static final Type<Byte[]> BYTE_ARRAY = new ArrayType<>(Type.BYTE);
|
||||
|
||||
public static final Type<byte[]> REMAINING_BYTES = new RemainingBytesType();
|
||||
|
||||
public static final Type<Short> UNSIGNED_BYTE = new UnsignedByteType();
|
||||
public static final Type<Short[]> UNSIGNED_BYTE_ARRAY = new ArrayType<>(Type.UNSIGNED_BYTE);
|
||||
|
||||
public static final Type<Boolean> BOOLEAN = new BooleanType();
|
||||
public static final Type<Boolean[]> BOOLEAN_ARRAY = new ArrayType<>(Type.BOOLEAN);
|
||||
/* Number Types */
|
||||
public static final Type<Integer> INT = new IntType();
|
||||
public static final Type<Integer[]> INT_ARRAY = new ArrayType<>(Type.INT);
|
||||
|
||||
public static final Type<Double> DOUBLE = new DoubleType();
|
||||
public static final Type<Double[]> DOUBLE_ARRAY = new ArrayType<>(Type.DOUBLE);
|
||||
|
||||
public static final Type<Long> LONG = new LongType();
|
||||
public static final Type<Long[]> LONG_ARRAY = new ArrayType<>(Type.LONG);
|
||||
|
||||
public static final Type<Float> FLOAT = new FloatType();
|
||||
public static final Type<Float[]> FLOAT_ARRAY = new ArrayType<>(Type.FLOAT);
|
||||
|
||||
public static final Type<Short> SHORT = new ShortType();
|
||||
public static final Type<Short[]> SHORT_ARRAY = new ArrayType<>(Type.SHORT);
|
||||
|
||||
public static final Type<Integer> UNSIGNED_SHORT = new UnsignedShortType();
|
||||
public static final Type<Integer[]> UNSIGNED_SHORT_ARRAY = new ArrayType<>(Type.UNSIGNED_SHORT);
|
||||
/* Other Types */
|
||||
public static final Type<String> STRING = new StringType();
|
||||
public static final Type<String[]> STRING_ARRAY = new ArrayType<>(Type.STRING);
|
||||
|
||||
public static final Type<UUID> UUID = new UUIDType();
|
||||
public static final Type<UUID[]> UUID_ARRAY = new ArrayType<>(Type.UUID);
|
||||
/* Variable Types */
|
||||
public static final Type<Integer> VAR_INT = new VarIntType();
|
||||
public static final Type<Integer[]> VAR_INT_ARRAY = new ArrayType<>(Type.VAR_INT);
|
||||
/* Special Types */
|
||||
public static final Type<Void> NOTHING = new VoidType(); // This is purely used for remapping.
|
||||
/* MC Types */
|
||||
public static final Type<Position> POSITION = new PositionType();
|
||||
public static final Type<EulerAngle> ROTATION = new EulerAngleType();
|
||||
public static final Type<Vector> VECTOR = new VectorType();
|
||||
public static final Type<CompoundTag> NBT = new NBTType();
|
||||
public static final Type<CompoundTag[]> NBT_ARRAY = new ArrayType<>(Type.NBT);
|
||||
|
||||
public static final Type<UUID> OPTIONAL_UUID = new OptUUIDType();
|
||||
public static final Type<Position> OPTIONAL_POSITION = new OptPositionType();
|
||||
|
||||
public static final Type<Item> ITEM = new ItemType();
|
||||
public static final Type<Item[]> ITEM_ARRAY = new ItemArrayType();
|
||||
/* Actual Class */
|
||||
|
||||
private final Class<? super T> outputClass;
|
||||
private final String typeName;
|
||||
|
||||
public Type(Class<? super T> outputClass) {
|
||||
this(outputClass.getSimpleName(), outputClass);
|
||||
}
|
||||
|
||||
public Type(String typeName, Class<? super T> outputClass) {
|
||||
this.outputClass = outputClass;
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
public Class<? extends Type> getBaseClass() {
|
||||
return this.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Type|" + getTypeName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package us.myles.ViaVersion.api.type;
|
||||
|
||||
public interface TypeConverter<T> {
|
||||
/**
|
||||
* Convert from a type to the current type
|
||||
*
|
||||
* @param o The input object
|
||||
* @return The converted type as an object
|
||||
*/
|
||||
T from(Object o);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
public class ArrayType<T> extends Type<T[]> {
|
||||
private final Type<T> elementType;
|
||||
|
||||
public ArrayType(Type<T> type) {
|
||||
super(type.getTypeName() + " Array", (Class<T[]>) getArrayClass(type.getOutputClass()));
|
||||
this.elementType = type;
|
||||
}
|
||||
|
||||
public static Class<?> getArrayClass(Class<?> componentType) {
|
||||
// Should only happen once per class init.
|
||||
return Array.newInstance(componentType, 0).getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] read(ByteBuf buffer) throws Exception {
|
||||
int amount = Type.VAR_INT.read(buffer);
|
||||
T[] array = (T[]) Array.newInstance(elementType.getOutputClass(), amount);
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
array[i] = elementType.read(buffer);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, T[] object) throws Exception {
|
||||
Type.VAR_INT.write(buffer, object.length);
|
||||
for (T o : object) {
|
||||
elementType.write(buffer, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class BooleanType extends Type<Boolean> implements TypeConverter<Boolean> {
|
||||
public BooleanType() {
|
||||
super(Boolean.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean read(ByteBuf buffer) {
|
||||
return buffer.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Boolean object) {
|
||||
buffer.writeBoolean(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue() == 1;
|
||||
}
|
||||
return (Boolean) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class ByteType extends Type<Byte> implements TypeConverter<Byte> {
|
||||
public ByteType() {
|
||||
super(Byte.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte read(ByteBuf buffer) {
|
||||
return buffer.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Byte object) {
|
||||
buffer.writeByte(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Byte from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).byteValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return (Boolean) o ? (byte) 1 : 0;
|
||||
}
|
||||
return (Byte) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class DoubleType extends Type<Double> implements TypeConverter<Double> {
|
||||
public DoubleType() {
|
||||
super(Double.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double read(ByteBuf buffer) {
|
||||
return buffer.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Double object) {
|
||||
buffer.writeDouble(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).doubleValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return (Boolean) o ? (byte) 1D : 0D;
|
||||
}
|
||||
return (Double) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class FloatType extends Type<Float> implements TypeConverter<Float> {
|
||||
public FloatType() {
|
||||
super(Float.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float read(ByteBuf buffer) {
|
||||
return buffer.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Float object) {
|
||||
buffer.writeFloat(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Float from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).floatValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1F : 0;
|
||||
}
|
||||
return (Float) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class IntType extends Type<Integer> implements TypeConverter<Integer> {
|
||||
public IntType() {
|
||||
super(Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(ByteBuf buffer) {
|
||||
return buffer.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Integer object) {
|
||||
buffer.writeInt(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1 : 0;
|
||||
}
|
||||
return (Integer) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class LongType extends Type<Long> implements TypeConverter<Long> {
|
||||
public LongType() {
|
||||
super(Long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long read(ByteBuf buffer) {
|
||||
return buffer.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Long object) {
|
||||
buffer.writeLong(object);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Long from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).longValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1L : 0;
|
||||
}
|
||||
return (Long) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class RemainingBytesType extends Type<byte[]> {
|
||||
public RemainingBytesType() {
|
||||
super(byte[].class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] read(ByteBuf buffer) {
|
||||
byte[] array = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, byte[] object) {
|
||||
buffer.writeBytes(object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class ShortType extends Type<Short> implements TypeConverter<Short> {
|
||||
public ShortType() {
|
||||
super(Short.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short read(ByteBuf buffer) {
|
||||
return buffer.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Short object) {
|
||||
buffer.writeShort(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).shortValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? (short) 1 : 0;
|
||||
}
|
||||
return (short) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class StringType extends Type<String> {
|
||||
public StringType() {
|
||||
super(String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String read(ByteBuf buffer) throws Exception {
|
||||
int len = Type.VAR_INT.read(buffer);
|
||||
Preconditions.checkArgument(len <= Short.MAX_VALUE, "Cannot receive string longer than Short.MAX_VALUE (got %s characters)", len);
|
||||
|
||||
byte[] b = new byte[len];
|
||||
buffer.readBytes(b);
|
||||
|
||||
return new String(b, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, String object) throws Exception {
|
||||
Preconditions.checkArgument(object.length() <= Short.MAX_VALUE, "Cannot send string longer than Short.MAX_VALUE (got %s characters)", object.length());
|
||||
|
||||
byte[] b = object.getBytes(Charsets.UTF_8);
|
||||
Type.VAR_INT.write(buffer, b.length);
|
||||
buffer.writeBytes(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class UUIDType extends Type<UUID> {
|
||||
public UUIDType() {
|
||||
super(UUID.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID read(ByteBuf buffer) {
|
||||
return new UUID(buffer.readLong(), buffer.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, UUID object) {
|
||||
buffer.writeLong(object.getMostSignificantBits());
|
||||
buffer.writeLong(object.getLeastSignificantBits());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class UnsignedByteType extends Type<Short> implements TypeConverter<Short> {
|
||||
public UnsignedByteType() {
|
||||
super("Unsigned Byte", Short.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short read(ByteBuf buffer) {
|
||||
return buffer.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Short object) {
|
||||
buffer.writeByte(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).shortValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? (short) 1 : 0;
|
||||
}
|
||||
return (short) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class UnsignedShortType extends Type<Integer> implements TypeConverter<Integer> {
|
||||
public UnsignedShortType() {
|
||||
super(Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(ByteBuf buffer) {
|
||||
return buffer.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Integer object) {
|
||||
buffer.writeShort(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1 : 0;
|
||||
}
|
||||
return (Integer) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class VarIntType extends Type<Integer> implements TypeConverter<Integer> {
|
||||
|
||||
public VarIntType() {
|
||||
super("VarInt", Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Integer object) {
|
||||
int part;
|
||||
while (true) {
|
||||
part = object & 0x7F;
|
||||
|
||||
object >>>= 7;
|
||||
if (object != 0) {
|
||||
part |= 0x80;
|
||||
}
|
||||
|
||||
buffer.writeByte(part);
|
||||
|
||||
if (object == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(ByteBuf buffer) {
|
||||
int out = 0;
|
||||
int bytes = 0;
|
||||
byte in;
|
||||
while (true) {
|
||||
in = buffer.readByte();
|
||||
|
||||
out |= (in & 0x7F) << (bytes++ * 7);
|
||||
|
||||
if (bytes > 5) { // 5 is maxBytes
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
|
||||
if ((in & 0x80) != 0x80) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer from(Object o) {
|
||||
if (o instanceof Number) {
|
||||
return ((Number) o).intValue();
|
||||
}
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o) ? 1 : 0;
|
||||
}
|
||||
return (Integer) o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package us.myles.ViaVersion.api.type.types;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.api.type.TypeConverter;
|
||||
|
||||
public class VoidType extends Type<Void> implements TypeConverter<Void> {
|
||||
public VoidType() {
|
||||
super(Void.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void read(ByteBuf buffer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Void object) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void from(Object o) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public abstract class BaseChunkType extends Type<Chunk> {
|
||||
public BaseChunkType() {
|
||||
super(Chunk.class);
|
||||
}
|
||||
|
||||
public BaseChunkType(String typeName) {
|
||||
super(typeName, Chunk.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getBaseClass() {
|
||||
return BaseChunkType.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.bukkit.util.EulerAngle;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class EulerAngleType extends Type<EulerAngle> {
|
||||
public EulerAngleType() {
|
||||
super(EulerAngle.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EulerAngle read(ByteBuf buffer) throws Exception {
|
||||
float x = Type.FLOAT.read(buffer);
|
||||
float y = Type.FLOAT.read(buffer);
|
||||
float z = Type.FLOAT.read(buffer);
|
||||
|
||||
return new EulerAngle(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, EulerAngle object) throws Exception {
|
||||
Type.FLOAT.write(buffer, (float) object.getX());
|
||||
Type.FLOAT.write(buffer, (float) object.getY());
|
||||
Type.FLOAT.write(buffer, (float) object.getZ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.item.Item;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class ItemArrayType extends Type<Item[]> {
|
||||
|
||||
public ItemArrayType() {
|
||||
super("Item Array", Item[].class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item[] read(ByteBuf buffer) throws Exception {
|
||||
int amount = Type.SHORT.read(buffer);
|
||||
Item[] array = new Item[amount];
|
||||
for (int i = 0; i < amount; i++) {
|
||||
array[i] = Type.ITEM.read(buffer);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Item[] object) throws Exception {
|
||||
Type.SHORT.write(buffer, (short) object.length);
|
||||
for (Item o : object) {
|
||||
Type.ITEM.write(buffer, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.item.Item;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class ItemType extends Type<Item> {
|
||||
public ItemType() {
|
||||
super(Item.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item read(ByteBuf buffer) throws Exception {
|
||||
short id = buffer.readShort();
|
||||
if (id < 0) {
|
||||
return null;
|
||||
} else {
|
||||
Item item = new Item();
|
||||
item.setId(id);
|
||||
item.setAmount(buffer.readByte());
|
||||
item.setData(buffer.readShort());
|
||||
item.setTag(Type.NBT.read(buffer));
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Item object) throws Exception {
|
||||
if (object == null) {
|
||||
buffer.writeShort(-1);
|
||||
} else {
|
||||
buffer.writeShort(object.getId());
|
||||
buffer.writeByte(object.getAmount());
|
||||
buffer.writeShort(object.getData());
|
||||
Type.NBT.write(buffer, object.getTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class MetaListTypeTemplate extends Type<List<Metadata>> {
|
||||
public MetaListTypeTemplate() {
|
||||
super("MetaData List", List.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getBaseClass() {
|
||||
return MetaListTypeTemplate.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public abstract class MetaTypeTemplate extends Type<Metadata> {
|
||||
public MetaTypeTemplate() {
|
||||
super("Metadata type", Metadata.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getBaseClass() {
|
||||
return MetaTypeTemplate.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import org.spacehq.opennbt.NBTIO;
|
||||
import org.spacehq.opennbt.tag.builtin.CompoundTag;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
||||
public class NBTType extends Type<CompoundTag> {
|
||||
public NBTType() {
|
||||
super(CompoundTag.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag read(ByteBuf buffer) throws Exception {
|
||||
Preconditions.checkArgument(buffer.readableBytes() <= 2097152, "Cannot read NBT (got %s bytes)", buffer.readableBytes());
|
||||
|
||||
int readerIndex = buffer.readerIndex();
|
||||
byte b = buffer.readByte();
|
||||
if (b == 0) {
|
||||
return null;
|
||||
} else {
|
||||
buffer.readerIndex(readerIndex);
|
||||
ByteBufInputStream bytebufStream = new ByteBufInputStream(buffer);
|
||||
try (DataInputStream dataInputStream = new DataInputStream(bytebufStream)) {
|
||||
return (CompoundTag) NBTIO.readTag(dataInputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, CompoundTag object) throws Exception {
|
||||
if (object == null) {
|
||||
buffer.writeByte(0);
|
||||
} else {
|
||||
ByteBufOutputStream bytebufStream = new ByteBufOutputStream(buffer);
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(bytebufStream);
|
||||
|
||||
NBTIO.writeTag(dataOutputStream, object);
|
||||
|
||||
dataOutputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class OptPositionType extends Type<Position> {
|
||||
public OptPositionType() {
|
||||
super(Position.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position read(ByteBuf buffer) throws Exception {
|
||||
boolean present = buffer.readBoolean();
|
||||
if (!present) return null;
|
||||
return Type.POSITION.read(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Position object) throws Exception {
|
||||
buffer.writeBoolean(object != null);
|
||||
if (object != null)
|
||||
Type.POSITION.write(buffer, object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class OptUUIDType extends Type<UUID> {
|
||||
public OptUUIDType() {
|
||||
super(UUID.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID read(ByteBuf buffer) {
|
||||
boolean present = buffer.readBoolean();
|
||||
if (!present) return null;
|
||||
return new UUID(buffer.readLong(), buffer.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, UUID object) {
|
||||
if (object == null) {
|
||||
buffer.writeBoolean(false);
|
||||
} else {
|
||||
buffer.writeBoolean(true);
|
||||
buffer.writeLong(object.getMostSignificantBits());
|
||||
buffer.writeLong(object.getLeastSignificantBits());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
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
|
||||
|
||||
return new Position(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Position object) {
|
||||
buffer.writeLong(((object.getX() & 0x3ffffff) << 38) | ((object.getY() & 0xfff) << 26) | (object.getZ() & 0x3ffffff));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.api.type.types.minecraft;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.bukkit.util.Vector;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
public class VectorType extends Type<Vector> {
|
||||
public VectorType() {
|
||||
super(Vector.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector read(ByteBuf buffer) throws Exception {
|
||||
int x = Type.INT.read(buffer);
|
||||
int y = Type.INT.read(buffer);
|
||||
int z = Type.INT.read(buffer);
|
||||
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Vector object) throws Exception {
|
||||
Type.INT.write(buffer, object.getBlockX());
|
||||
Type.INT.write(buffer, object.getBlockY());
|
||||
Type.INT.write(buffer, object.getBlockZ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package us.myles.ViaVersion.api.type.types.version;
|
||||
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.types.minecraft.MetaTypeTemplate;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.MetadataTypes;
|
||||
|
||||
public class Metadata1_8Type extends MetaTypeTemplate {
|
||||
|
||||
@Override
|
||||
public Metadata read(ByteBuf buffer) throws Exception {
|
||||
byte item = buffer.readByte();
|
||||
if (item == 127) return null; // end of metadata
|
||||
int typeID = (item & 0xE0) >> 5;
|
||||
MetadataTypes type = MetadataTypes.byId(typeID);
|
||||
int id = item & 0x1F;
|
||||
return new Metadata(id, typeID, type.getType(), type.getType().read(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Metadata object) throws Exception {
|
||||
throw new UnsupportedOperationException("1.8 Metadata writing is not implemented!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types.version;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.types.minecraft.MetaTypeTemplate;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.NewType;
|
||||
|
||||
public class Metadata1_9Type extends MetaTypeTemplate {
|
||||
|
||||
@Override
|
||||
public Metadata read(ByteBuf buffer) throws Exception {
|
||||
short index = buffer.readUnsignedByte();
|
||||
|
||||
if (index == 0xff) return null; //End of metadata
|
||||
NewType type = NewType.byId(buffer.readByte());
|
||||
|
||||
return new Metadata(index, type.getTypeID(), type.getType(), type.getType().read(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, Metadata object) throws Exception {
|
||||
if (object == null) {
|
||||
buffer.writeByte(255);
|
||||
} else {
|
||||
buffer.writeByte(object.getId());
|
||||
buffer.writeByte(object.getTypeID());
|
||||
object.getType().write(buffer, object.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.api.type.types.version;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.types.minecraft.MetaListTypeTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MetadataList1_8Type extends MetaListTypeTemplate {
|
||||
|
||||
@Override
|
||||
public List<Metadata> read(ByteBuf buffer) throws Exception {
|
||||
List<Metadata> list = new ArrayList<>();
|
||||
Metadata m;
|
||||
do {
|
||||
m = Types1_8.METADATA.read(buffer);
|
||||
if (m != null) {
|
||||
list.add(m);
|
||||
}
|
||||
} while (m != null);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, List<Metadata> object) throws Exception {
|
||||
throw new UnsupportedOperationException("1.8 MetadataList writing is not implemented");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package us.myles.ViaVersion.api.type.types.version;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.types.minecraft.MetaListTypeTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MetadataList1_9Type extends MetaListTypeTemplate {
|
||||
|
||||
@Override
|
||||
public List<Metadata> read(ByteBuf buffer) throws Exception {
|
||||
List<Metadata> list = new ArrayList<>();
|
||||
Metadata meta;
|
||||
do {
|
||||
meta = Types1_9.METADATA.read(buffer);
|
||||
if (meta != null)
|
||||
list.add(meta);
|
||||
} while (meta != null);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buffer, List<Metadata> object) throws Exception {
|
||||
for (Metadata m : object)
|
||||
Types1_9.METADATA.write(buffer, m);
|
||||
|
||||
// Write end of list
|
||||
Types1_9.METADATA.write(buffer, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package us.myles.ViaVersion.api.type.types.version;
|
||||
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Types1_8 {
|
||||
/**
|
||||
* Metadata list type for 1.8
|
||||
*/
|
||||
public static Type<List<Metadata>> METADATA_LIST = new MetadataList1_8Type();
|
||||
|
||||
/**
|
||||
* Metadata type for 1.8
|
||||
*/
|
||||
public static Type<Metadata> METADATA = new Metadata1_8Type();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package us.myles.ViaVersion.api.type.types.version;
|
||||
|
||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Types1_9 {
|
||||
/**
|
||||
* Metadata list type for 1.9
|
||||
*/
|
||||
public static final Type<List<Metadata>> METADATA_LIST = new MetadataList1_9Type();
|
||||
|
||||
/**
|
||||
* Metadata type for 1.9
|
||||
*/
|
||||
public static final Type<Metadata> METADATA = new Metadata1_9Type();
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package us.myles.ViaVersion.armor;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.inventory.CraftingInventory;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.packets.PacketType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static us.myles.ViaVersion.util.PacketUtil.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ArmorListener implements Listener {
|
||||
|
||||
private static final UUID ARMOR_ATTRIBUTE = UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150");
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
public static void sendArmorUpdate(Player player) {
|
||||
int armor = ArmorType.calculateArmorPoints(player.getInventory().getArmorContents());
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
writeVarInt(PacketType.PLAY_ENTITY_PROPERTIES.getNewPacketID(), buf);
|
||||
writeVarInt(player.getEntityId(), buf);
|
||||
buf.writeInt(1); // only 1 property
|
||||
writeString("generic.armor", buf);
|
||||
buf.writeDouble(0); //default 0 armor
|
||||
writeVarInt(1, buf); // 1 modifier
|
||||
writeUUID(ARMOR_ATTRIBUTE, buf); // armor modifier uuid
|
||||
buf.writeDouble((double) armor); // the modifier value
|
||||
buf.writeByte(0); // the modifier operation, 0 is add number
|
||||
|
||||
ViaVersion.getInstance().sendRawPacket(player, buf);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onInventoryClick(InventoryClickEvent e) {
|
||||
HumanEntity human = e.getWhoClicked();
|
||||
if (human instanceof Player && e.getInventory() instanceof CraftingInventory) {
|
||||
final Player player = (Player) human;
|
||||
if (ViaVersion.getInstance().isPorted(player)) {
|
||||
if (e.getCurrentItem() != null) {
|
||||
if (ArmorType.isArmor(e.getCurrentItem().getType())) {
|
||||
sendDelayedArmorUpdate(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (e.getRawSlot() >= 5 && e.getRawSlot() <= 8) {
|
||||
sendDelayedArmorUpdate(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onInteract(PlayerInteractEvent e) {
|
||||
if (e.getItem() != null) {
|
||||
if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) {
|
||||
if (ArmorType.isArmor(e.getMaterial())) {
|
||||
final Player player = e.getPlayer();
|
||||
// Due to odd bugs it's 3 ticks later
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ViaVersion.getInstance().isPorted(player)) {
|
||||
sendArmorUpdate(player);
|
||||
}
|
||||
}
|
||||
}, 3L);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
sendDelayedArmorUpdate(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
if (ViaVersion.getInstance().isPorted(e.getPlayer()))
|
||||
sendDelayedArmorUpdate(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
if (ViaVersion.getInstance().isPorted(e.getPlayer()))
|
||||
sendArmorUpdate(e.getPlayer());
|
||||
}
|
||||
|
||||
public void sendDelayedArmorUpdate(final Player player) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ViaVersion.getInstance().isPorted(player)) {
|
||||
sendArmorUpdate(player);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package us.myles.ViaVersion.boss;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -11,9 +12,9 @@ import us.myles.ViaVersion.api.boss.BossBar;
|
||||
import us.myles.ViaVersion.api.boss.BossColor;
|
||||
import us.myles.ViaVersion.api.boss.BossFlag;
|
||||
import us.myles.ViaVersion.api.boss.BossStyle;
|
||||
import us.myles.ViaVersion.packets.PacketType;
|
||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -42,17 +43,18 @@ public class ViaBossBar implements BossBar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
Validate.notNull(title, "Title cannot be null");
|
||||
public BossBar setTitle(@NonNull String title) {
|
||||
this.title = title;
|
||||
sendPacket(UpdateAction.UPDATE_TITLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHealth(float health) {
|
||||
public BossBar setHealth(float health) {
|
||||
Validate.isTrue((health >= 0 && health <= 1), "Health must be between 0 and 1");
|
||||
this.health = health;
|
||||
sendPacket(UpdateAction.UPDATE_HEALTH);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,52 +63,68 @@ public class ViaBossBar implements BossBar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(BossColor color) {
|
||||
Validate.notNull(color, "Color cannot be null");
|
||||
public BossBar setColor(@NonNull BossColor color) {
|
||||
this.color = color;
|
||||
sendPacket(UpdateAction.UPDATE_STYLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(BossStyle style) {
|
||||
Validate.notNull(style, "Style cannot be null");
|
||||
public BossBar setStyle(@NonNull BossStyle style) {
|
||||
this.style = style;
|
||||
sendPacket(UpdateAction.UPDATE_STYLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlayer(Player player) {
|
||||
if (player != null && !players.contains(player.getUniqueId())) {
|
||||
players.add(player.getUniqueId());
|
||||
public BossBar addPlayer(@NonNull Player player) {
|
||||
return addPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar addPlayer(UUID player) {
|
||||
if (!players.contains(player)) {
|
||||
players.add(player);
|
||||
if (visible)
|
||||
sendPacket(player.getUniqueId(), getPacket(UpdateAction.ADD));
|
||||
sendPacket(player, getPacket(UpdateAction.ADD));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePlayer(Player player) {
|
||||
if (player != null && players.contains(player.getUniqueId())) {
|
||||
public BossBar removePlayer(@NonNull Player player) {
|
||||
if (players.contains(player.getUniqueId())) {
|
||||
players.remove(player.getUniqueId());
|
||||
sendPacket(player.getUniqueId(), getPacket(UpdateAction.REMOVE));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFlag(BossFlag flag) {
|
||||
public BossBar addPlayers(@NonNull Player... players) {
|
||||
for (Player p : players)
|
||||
addPlayer(p);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar addFlag(@NonNull BossFlag flag) {
|
||||
if (!hasFlag(flag))
|
||||
flags.add(flag);
|
||||
sendPacket(UpdateAction.UPDATE_FLAGS);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFlag(BossFlag flag) {
|
||||
public BossBar removeFlag(@NonNull BossFlag flag) {
|
||||
if (hasFlag(flag))
|
||||
flags.remove(flag);
|
||||
sendPacket(UpdateAction.UPDATE_FLAGS);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFlag(BossFlag flag) {
|
||||
public boolean hasFlag(@NonNull BossFlag flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
||||
@@ -116,13 +134,15 @@ public class ViaBossBar implements BossBar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
public BossBar show() {
|
||||
setVisible(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
public BossBar hide() {
|
||||
setVisible(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,50 +158,57 @@ public class ViaBossBar implements BossBar {
|
||||
}
|
||||
|
||||
private void sendPacket(UpdateAction action) {
|
||||
ByteBuf buf = getPacket(action);
|
||||
for (UUID uuid : new ArrayList<>(players))
|
||||
for (UUID uuid : new ArrayList<>(players)) {
|
||||
ByteBuf buf = getPacket(action);
|
||||
sendPacket(uuid, buf);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPacket(UUID uuid, ByteBuf buf) {
|
||||
if (!ViaVersion.getInstance().isPorted(uuid)) {
|
||||
if (!ViaVersion.getInstance().isPorted(uuid) || !(ViaVersion.getInstance().getPlayerVersion(uuid) >= ProtocolVersion.v1_9.getId())) {
|
||||
players.remove(uuid);
|
||||
buf.release();
|
||||
return;
|
||||
}
|
||||
ViaVersion.getInstance().sendRawPacket(uuid, buf);
|
||||
}
|
||||
|
||||
private ByteBuf getPacket(UpdateAction action) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
PacketUtil.writeVarInt(PacketType.PLAY_BOSS_BAR.getNewPacketID(), buf);
|
||||
PacketUtil.writeUUID(uuid, buf);
|
||||
PacketUtil.writeVarInt(action.getId(), buf);
|
||||
switch (action) {
|
||||
case ADD:
|
||||
PacketUtil.writeString(fixJson(title), buf);
|
||||
buf.writeFloat(health);
|
||||
PacketUtil.writeVarInt(color.getId(), buf);
|
||||
PacketUtil.writeVarInt(style.getId(), buf);
|
||||
buf.writeByte(flagToBytes());
|
||||
break;
|
||||
case REMOVE:
|
||||
break;
|
||||
case UPDATE_HEALTH:
|
||||
buf.writeFloat(health);
|
||||
break;
|
||||
case UPDATE_TITLE:
|
||||
PacketUtil.writeString(fixJson(title), buf);
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
PacketUtil.writeVarInt(color.getId(), buf);
|
||||
PacketUtil.writeVarInt(style.getId(), buf);
|
||||
break;
|
||||
case UPDATE_FLAGS:
|
||||
buf.writeByte(flagToBytes());
|
||||
break;
|
||||
}
|
||||
try {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
Type.VAR_INT.write(buf, 0x0C); // Boss bar packet
|
||||
Type.UUID.write(buf, uuid);
|
||||
Type.VAR_INT.write(buf, action.getId());
|
||||
switch (action) {
|
||||
case ADD:
|
||||
Type.STRING.write(buf, fixJson(title));
|
||||
buf.writeFloat(health);
|
||||
Type.VAR_INT.write(buf, color.getId());
|
||||
Type.VAR_INT.write(buf, style.getId());
|
||||
buf.writeByte(flagToBytes());
|
||||
break;
|
||||
case REMOVE:
|
||||
break;
|
||||
case UPDATE_HEALTH:
|
||||
buf.writeFloat(health);
|
||||
break;
|
||||
case UPDATE_TITLE:
|
||||
Type.STRING.write(buf, fixJson(title));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
Type.VAR_INT.write(buf, color.getId());
|
||||
Type.VAR_INT.write(buf, style.getId());
|
||||
break;
|
||||
case UPDATE_FLAGS:
|
||||
buf.writeByte(flagToBytes());
|
||||
break;
|
||||
}
|
||||
|
||||
return buf;
|
||||
return buf;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int flagToBytes() {
|
||||
@@ -192,7 +219,7 @@ public class ViaBossBar implements BossBar {
|
||||
}
|
||||
|
||||
private String fixJson(String text) {
|
||||
return OutgoingTransformer.fixJson(text);
|
||||
return Protocol1_9TO1_8.fixJson(text);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class Chunk {
|
||||
private final int x;
|
||||
private final int z;
|
||||
private final boolean groundUp;
|
||||
private final int primaryBitmask;
|
||||
private final ChunkSection[] sections;
|
||||
private final byte[] biomeData;
|
||||
private boolean unloadPacket = false;
|
||||
|
||||
/**
|
||||
* Chunk unload.
|
||||
*
|
||||
* @param x coord
|
||||
* @param z coord
|
||||
*/
|
||||
protected Chunk(int x, int z) {
|
||||
this(x, z, true, 0, new ChunkSection[16], null);
|
||||
this.unloadPacket = true;
|
||||
}
|
||||
|
||||
public boolean hasBiomeData() {
|
||||
return biomeData != null && groundUp;
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
package us.myles.ViaVersion.chunks;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.bukkit.Bukkit;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil.ClassReflection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ChunkManager {
|
||||
/**
|
||||
* Amount of sections in a chunk.
|
||||
*/
|
||||
private static final int SECTION_COUNT = 16;
|
||||
/**
|
||||
* size of each chunk section (16x16x16).
|
||||
*/
|
||||
private static final int SECTION_SIZE = 16;
|
||||
/**
|
||||
* Length of biome data.
|
||||
*/
|
||||
private static final int BIOME_DATA_LENGTH = 256;
|
||||
|
||||
private final ConnectionInfo info;
|
||||
private final Set<Long> loadedChunks = Sets.newConcurrentHashSet();
|
||||
private final Set<Long> bulkChunks = Sets.newConcurrentHashSet();
|
||||
|
||||
// Reflection
|
||||
private static ClassReflection mapChunkBulkRef;
|
||||
private static ClassReflection mapChunkRef;
|
||||
|
||||
static {
|
||||
try {
|
||||
mapChunkBulkRef = new ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunkBulk"));
|
||||
mapChunkRef = new ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunk"));
|
||||
} catch(Exception e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to initialise chunk reflection", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkManager(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a map chunk bulk in to separate map chunk packets.
|
||||
* These packets are registered so that they will never be seen as unload packets.
|
||||
*
|
||||
* @param packet to transform
|
||||
* @return List of chunk data packets
|
||||
*/
|
||||
public List<Object> transformMapChunkBulk(Object packet) {
|
||||
List<Object> list = Lists.newArrayList();
|
||||
try {
|
||||
int[] xcoords = mapChunkBulkRef.getFieldValue("a", packet, int[].class);
|
||||
int[] zcoords = mapChunkBulkRef.getFieldValue("b", packet, int[].class);
|
||||
Object[] chunkMaps = mapChunkBulkRef.getFieldValue("c", packet, Object[].class);
|
||||
for(int i = 0; i < chunkMaps.length; i++) {
|
||||
int x = xcoords[i];
|
||||
int z = zcoords[i];
|
||||
Object chunkMap = chunkMaps[i];
|
||||
Object chunkPacket = mapChunkRef.newInstance();
|
||||
mapChunkRef.setFieldValue("a", chunkPacket, x);
|
||||
mapChunkRef.setFieldValue("b", chunkPacket, z);
|
||||
mapChunkRef.setFieldValue("c", chunkPacket, chunkMap);
|
||||
mapChunkRef.setFieldValue("d", chunkPacket, true); // Chunk bulk chunks are always ground-up
|
||||
bulkChunks.add(toLong(x, z)); // Store for later
|
||||
list.add(chunkPacket);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to transform chunk bulk", e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read chunk from 1.8 chunk data.
|
||||
*
|
||||
* @param input data
|
||||
* @return Chunk
|
||||
*/
|
||||
public Chunk readChunk(ByteBuf input) {
|
||||
// Primary data
|
||||
int chunkX = input.readInt();
|
||||
int chunkZ = input.readInt();
|
||||
long chunkHash = toLong(chunkX, chunkZ);
|
||||
boolean groundUp = input.readByte() != 0;
|
||||
int bitmask = input.readUnsignedShort();
|
||||
int dataLength = PacketUtil.readVarInt(input);
|
||||
|
||||
// Data to be read
|
||||
BitSet usedSections = new BitSet(16);
|
||||
ChunkSection[] sections = new ChunkSection[16];
|
||||
byte[] biomeData = null;
|
||||
|
||||
// Calculate section count from bitmask
|
||||
for(int i = 0; i < 16; i++) {
|
||||
if((bitmask & (1 << i)) != 0) {
|
||||
usedSections.set(i);
|
||||
}
|
||||
}
|
||||
int sectionCount = usedSections.cardinality(); // the amount of sections set
|
||||
|
||||
// If the chunk is from a chunk bulk, it is never an unload packet
|
||||
// Other wise, if it has no data, it is :)
|
||||
boolean isBulkPacket = bulkChunks.remove(chunkHash);
|
||||
if(sectionCount == 0 && groundUp && !isBulkPacket && loadedChunks.contains(chunkHash)) {
|
||||
// This is a chunk unload packet
|
||||
loadedChunks.remove(chunkHash);
|
||||
return new Chunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
int startIndex = input.readerIndex();
|
||||
loadedChunks.add(chunkHash); // mark chunk as loaded
|
||||
|
||||
// Read blocks
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
if(!usedSections.get(i)) continue; // Section not set
|
||||
ChunkSection section = new ChunkSection();
|
||||
sections[i] = section;
|
||||
|
||||
// Read block data and convert to short buffer
|
||||
byte[] blockData = new byte[ChunkSection.SIZE * 2];
|
||||
input.readBytes(blockData);
|
||||
ShortBuffer blockBuf = ByteBuffer.wrap(blockData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
|
||||
for(int j = 0; j < ChunkSection.SIZE; j++) {
|
||||
int mask = blockBuf.get();
|
||||
int type = mask >> 4;
|
||||
int data = mask & 0xF;
|
||||
section.setBlock(j, type, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Read block light
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
if(!usedSections.get(i)) continue; // Section not set, has no light
|
||||
byte[] blockLightArray = new byte[ChunkSection.LIGHT_LENGTH];
|
||||
input.readBytes(blockLightArray);
|
||||
sections[i].setBlockLight(blockLightArray);
|
||||
}
|
||||
|
||||
// Read sky light
|
||||
int bytesLeft = dataLength - (input.readerIndex() - startIndex);
|
||||
if(bytesLeft >= ChunkSection.LIGHT_LENGTH) {
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
if(!usedSections.get(i)) continue; // Section not set, has no light
|
||||
byte[] skyLightArray = new byte[ChunkSection.LIGHT_LENGTH];
|
||||
input.readBytes(skyLightArray);
|
||||
sections[i].setSkyLight(skyLightArray);
|
||||
bytesLeft -= ChunkSection.LIGHT_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
// Read biome data
|
||||
if(bytesLeft >= BIOME_DATA_LENGTH) {
|
||||
biomeData = new byte[BIOME_DATA_LENGTH];
|
||||
input.readBytes(biomeData);
|
||||
bytesLeft -= BIOME_DATA_LENGTH;
|
||||
}
|
||||
|
||||
// Check remaining bytes
|
||||
if(bytesLeft > 0) {
|
||||
Bukkit.getLogger().log(Level.WARNING, bytesLeft + " Bytes left after reading chunk! (" + groundUp + ")");
|
||||
}
|
||||
|
||||
// Return chunk
|
||||
return new Chunk(chunkX, chunkZ, groundUp, bitmask, sections, biomeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write chunk over 1.9 protocol.
|
||||
*
|
||||
* @param chunk chunk
|
||||
* @param output output
|
||||
*/
|
||||
public void writeChunk(Chunk chunk, ByteBuf output) {
|
||||
if(chunk.isUnloadPacket()) {
|
||||
output.clear();
|
||||
PacketUtil.writeVarInt(0x1D, output);
|
||||
}
|
||||
|
||||
// Write primary info
|
||||
output.writeInt(chunk.getX());
|
||||
output.writeInt(chunk.getZ());
|
||||
if(chunk.isUnloadPacket()) return;
|
||||
output.writeByte(chunk.isGroundUp() ? 0x01 : 0x00);
|
||||
PacketUtil.writeVarInt(chunk.getPrimaryBitmask(), output);
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
for(int i = 0; i < SECTION_COUNT; i++) {
|
||||
ChunkSection section = chunk.getSections()[i];
|
||||
if(section == null) continue; // Section not set
|
||||
section.writeBlocks(buf);
|
||||
section.writeBlockLight(buf);
|
||||
if(!section.hasSkyLight()) continue; // No sky light, we're done here.
|
||||
section.writeSkyLight(buf);
|
||||
}
|
||||
buf.readerIndex(0);
|
||||
PacketUtil.writeVarInt(buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0), output);
|
||||
output.writeBytes(buf);
|
||||
buf.release(); // release buffer
|
||||
|
||||
// Write biome data
|
||||
if(chunk.hasBiomeData()) {
|
||||
output.writeBytes(chunk.getBiomeData());
|
||||
}
|
||||
}
|
||||
|
||||
private static long toLong(int msw, int lsw) {
|
||||
return ((long) msw << 32) + lsw - -2147483648L;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package us.myles.ViaVersion.classgenerator;
|
||||
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
|
||||
import us.myles.ViaVersion.handlers.ViaEncodeHandler;
|
||||
|
||||
public class BasicHandlerConstructor implements HandlerConstructor {
|
||||
@Override
|
||||
public ViaEncodeHandler newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
|
||||
return new ViaEncodeHandler(info, minecraftEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaDecodeHandler newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
|
||||
return new ViaDecodeHandler(info, minecraftDecoder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package us.myles.ViaVersion.classgenerator;
|
||||
|
||||
import javassist.*;
|
||||
import javassist.expr.ConstructorCall;
|
||||
import javassist.expr.ExprEditor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
|
||||
import us.myles.ViaVersion.handlers.ViaEncodeHandler;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
public class ClassGenerator {
|
||||
private static HandlerConstructor constructor = new BasicHandlerConstructor();
|
||||
private static String psPackage = null;
|
||||
|
||||
public static HandlerConstructor getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
public static void generate() {
|
||||
if (ViaVersion.getInstance().isCompatSpigotBuild() || ViaVersion.getInstance().isProtocolSupport()) {
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
for (Plugin p : Bukkit.getPluginManager().getPlugins()) {
|
||||
pool.insertClassPath(new LoaderClassPath(p.getClass().getClassLoader()));
|
||||
}
|
||||
|
||||
if (ViaVersion.getInstance().isCompatSpigotBuild()) {
|
||||
Class decodeSuper = ReflectionUtil.nms("PacketDecoder");
|
||||
Class encodeSuper = ReflectionUtil.nms("PacketEncoder");
|
||||
// Generate the classes
|
||||
addSpigotCompatibility(pool, ViaDecodeHandler.class, decodeSuper);
|
||||
addSpigotCompatibility(pool, ViaEncodeHandler.class, encodeSuper);
|
||||
} else {
|
||||
Class decodeSuper = Class.forName(getPSPackage() + ".wrapped.WrappedDecoder");
|
||||
Class encodeSuper = Class.forName(getPSPackage() + ".wrapped.WrappedEncoder");
|
||||
// Generate the classes
|
||||
addPSCompatibility(pool, ViaDecodeHandler.class, decodeSuper);
|
||||
addPSCompatibility(pool, ViaEncodeHandler.class, encodeSuper);
|
||||
}
|
||||
|
||||
|
||||
// Implement Constructor
|
||||
CtClass generated = pool.makeClass("us.myles.ViaVersion.classgenerator.generated.GeneratedConstructor");
|
||||
CtClass handlerInterface = pool.get(HandlerConstructor.class.getName());
|
||||
|
||||
generated.setInterfaces(new CtClass[]{handlerInterface});
|
||||
// Import required classes
|
||||
pool.importPackage("us.myles.ViaVersion.classgenerator.generated");
|
||||
pool.importPackage("us.myles.ViaVersion.classgenerator");
|
||||
pool.importPackage("us.myles.ViaVersion.api.data");
|
||||
pool.importPackage("io.netty.handler.codec");
|
||||
// Implement Methods
|
||||
generated.addMethod(CtMethod.make("public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {\n" +
|
||||
" return new ViaEncodeHandler(info, minecraftEncoder);\n" +
|
||||
" }", generated));
|
||||
generated.addMethod(CtMethod.make("public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {\n" +
|
||||
" return new ViaDecodeHandler(info, minecraftDecoder);\n" +
|
||||
" }", generated));
|
||||
|
||||
constructor = (HandlerConstructor) generated.toClass(HandlerConstructor.class.getClassLoader()).newInstance();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Class addSpigotCompatibility(ClassPool pool, Class input, Class superclass) {
|
||||
String newName = "us.myles.ViaVersion.classgenerator.generated." + input.getSimpleName();
|
||||
|
||||
try {
|
||||
CtClass generated = pool.getAndRename(input.getName(), newName);
|
||||
if (superclass != null) {
|
||||
CtClass toExtend = pool.get(superclass.getName());
|
||||
generated.setSuperclass(toExtend);
|
||||
|
||||
// If it's NMS satisfy constructor.
|
||||
if (superclass.getName().startsWith("net.minecraft")) {
|
||||
// Modify constructor to call super
|
||||
if (generated.getConstructors().length != 0) {
|
||||
generated.getConstructors()[0].instrument(new ExprEditor() {
|
||||
@Override
|
||||
public void edit(ConstructorCall c) throws CannotCompileException {
|
||||
if (c.isSuper()) {
|
||||
// Constructor for both has a stats thing.
|
||||
c.replace("super(null);");
|
||||
}
|
||||
super.edit(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return generated.toClass(HandlerConstructor.class.getClassLoader());
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class addPSCompatibility(ClassPool pool, Class input, Class superclass) {
|
||||
String newName = "us.myles.ViaVersion.classgenerator.generated." + input.getSimpleName();
|
||||
|
||||
try {
|
||||
CtClass generated = pool.getAndRename(input.getName(), newName);
|
||||
if (superclass != null) {
|
||||
CtClass toExtend = pool.get(superclass.getName());
|
||||
generated.setSuperclass(toExtend);
|
||||
|
||||
// Override setRealEncoder / setRealDecoder
|
||||
pool.importPackage(getPSPackage());
|
||||
pool.importPackage(getPSPackage() + ".wrapped");
|
||||
if (superclass.getName().endsWith("Decoder")) {
|
||||
// Decoder
|
||||
generated.addMethod(CtMethod.make("public void setRealDecoder(IPacketDecoder dec) {\n" +
|
||||
" WrappedDecoder decoder = new WrappedDecoder();" +
|
||||
" decoder.setRealDecoder(dec);\n" +
|
||||
" this.minecraftDecoder = decoder;\n" +
|
||||
" }", generated));
|
||||
} else {
|
||||
// Encoder
|
||||
generated.addMethod(CtMethod.make("public void setRealEncoder(IPacketEncoder enc) {\n" +
|
||||
" WrappedEncoder encoder = new WrappedEncoder();" +
|
||||
" encoder.setRealEncoder(enc);\n" +
|
||||
" this.minecraftEncoder = encoder;\n" +
|
||||
" }", generated));
|
||||
}
|
||||
}
|
||||
return generated.toClass(HandlerConstructor.class.getClassLoader());
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getPSPackage() {
|
||||
if (psPackage == null) {
|
||||
try {
|
||||
Class.forName("protocolsupport.protocol.core.IPacketDecoder");
|
||||
psPackage = "protocolsupport.protocol.core";
|
||||
} catch (ClassNotFoundException e) {
|
||||
try {
|
||||
Class.forName("protocolsupport.protocol.pipeline.IPacketDecoder");
|
||||
psPackage = "protocolsupport.protocol.pipeline";
|
||||
} catch (ClassNotFoundException e1) {
|
||||
psPackage = "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
return psPackage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package us.myles.ViaVersion.classgenerator;
|
||||
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
|
||||
public interface HandlerConstructor {
|
||||
public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder);
|
||||
|
||||
public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package us.myles.ViaVersion.commands;
|
||||
|
||||
import lombok.NonNull;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.api.command.ViaVersionCommand;
|
||||
import us.myles.ViaVersion.commands.defaultsubs.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ViaCommandHandler implements ViaVersionCommand, CommandExecutor, TabCompleter {
|
||||
private Map<String, ViaSubCommand> commandMap;
|
||||
|
||||
public ViaCommandHandler() {
|
||||
commandMap = new HashMap<>();
|
||||
try {
|
||||
registerDefaults();
|
||||
} catch (Exception e) {
|
||||
//ignore never throws exception because it doesn't exists
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSubCommand(@NonNull ViaSubCommand command) throws Exception {
|
||||
Validate.isTrue(command.name().matches("^[a-z0-9_-]{3,15}$"), command.name() + " is not a valid subcommand name");
|
||||
if (hasSubCommand(command.name()))
|
||||
throw new Exception("ViaSubCommand " + command.name() + " does already exists!"); //Maybe another exception later.
|
||||
commandMap.put(command.name().toLowerCase(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubCommand(String name) {
|
||||
return commandMap.containsKey(name.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaSubCommand getSubCommand(String name) {
|
||||
return commandMap.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String arg, String[] args) {
|
||||
if (args.length == 0) {
|
||||
showHelp(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasSubCommand(args[0])) {
|
||||
sender.sendMessage(color("&cThis command is not found"));
|
||||
showHelp(sender);
|
||||
return false;
|
||||
}
|
||||
ViaSubCommand handler = getSubCommand(args[0]);
|
||||
|
||||
if (!hasPermission(sender, handler.permission())) {
|
||||
sender.sendMessage(color("&cYou are not allowed to use this command!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
boolean result = handler.execute(sender, subArgs);
|
||||
if (!result)
|
||||
sender.sendMessage("Usage: /viaversion " + handler.usage());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String arg, String[] args) {
|
||||
Set<ViaSubCommand> allowed = calculateAllowedCommands(sender);
|
||||
List<String> output = new ArrayList<>();
|
||||
|
||||
//SubCommands tabcomplete
|
||||
if (args.length == 1) {
|
||||
if (!args[0].equals("")) {
|
||||
for (ViaSubCommand sub : allowed)
|
||||
if (sub.name().toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
output.add(sub.name());
|
||||
} else {
|
||||
for (ViaSubCommand sub : allowed)
|
||||
output.add(sub.name());
|
||||
}
|
||||
}
|
||||
//Let the SubCommand handle it
|
||||
else if (args.length >= 2) {
|
||||
if (getSubCommand(args[0]) != null) {
|
||||
ViaSubCommand sub = getSubCommand(args[0]);
|
||||
if (!allowed.contains(sub))
|
||||
return output;
|
||||
|
||||
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
|
||||
List<String> tab = sub.onTabComplete(sender, subArgs);
|
||||
Collections.sort(tab);
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public void showHelp(CommandSender sender) {
|
||||
Set<ViaSubCommand> allowed = calculateAllowedCommands(sender);
|
||||
if (allowed.size() == 0) {
|
||||
sender.sendMessage(color("&cYou are not allowed to use this command!"));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
|
||||
sender.sendMessage(color("&6Commands:"));
|
||||
for (ViaSubCommand cmd : allowed)
|
||||
sender.sendMessage(color(String.format("&2/viaversion %s &7- &6%s", cmd.usage(), cmd.description())));
|
||||
allowed.clear();
|
||||
}
|
||||
|
||||
private Set<ViaSubCommand> calculateAllowedCommands(CommandSender sender) {
|
||||
Set<ViaSubCommand> cmds = new HashSet<>();
|
||||
for (ViaSubCommand sub : commandMap.values())
|
||||
if (hasPermission(sender, sub.permission()))
|
||||
cmds.add(sub);
|
||||
return cmds;
|
||||
}
|
||||
|
||||
private boolean hasPermission(CommandSender sender, String permission) {
|
||||
return permission == null || sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
private void registerDefaults() throws Exception {
|
||||
registerSubCommand(new ListSubCmd());
|
||||
registerSubCommand(new PPSSubCmd());
|
||||
registerSubCommand(new DebugSubCmd());
|
||||
registerSubCommand(new DisplayLeaksSubCmd());
|
||||
registerSubCommand(new DontBugMeSubCmd());
|
||||
registerSubCommand(new AutoTeamSubCmd());
|
||||
registerSubCommand(new HelpSubCmd());
|
||||
registerSubCommand(new ReloadSubCmd());
|
||||
}
|
||||
|
||||
public static String color(String string) {
|
||||
try {
|
||||
string = ChatColor.translateAlternateColorCodes('&', string); //Dont replace all & with $ like we did before.
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public static void sendMessage(@NonNull CommandSender sender, String message, Object... args) {
|
||||
sender.sendMessage(color(args == null ? message : String.format(message, args)));
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package us.myles.ViaVersion.commands;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by fillefilip8 on 2016-03-03.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ViaVersionCommand implements CommandExecutor {
|
||||
|
||||
private final ViaVersionPlugin plugin;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (sender.hasPermission("viaversion.admin")) {
|
||||
if (args.length == 0) {
|
||||
sendHelp(sender);
|
||||
} else if (args.length == 1) {
|
||||
if (args[0].equalsIgnoreCase("list")) {
|
||||
List<String> portedPlayers = new ArrayList<>();
|
||||
List<String> normalPlayers = new ArrayList<>();
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (ViaVersion.getInstance().isPorted(p)) {
|
||||
portedPlayers.add(p.getName());
|
||||
} else {
|
||||
normalPlayers.add(p.getName());
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(color("&8[&61.9&8]: &b" + portedPlayers.toString()));
|
||||
sender.sendMessage(color("&8[&61.8&8]: &b" + normalPlayers.toString()));
|
||||
} else if (args[0].equalsIgnoreCase("debug")) {
|
||||
plugin.setDebug(!plugin.isDebug());
|
||||
sender.sendMessage(color("&6Debug mode is now " + (plugin.isDebug() ? "&aenabled" : "&cdisabled")));
|
||||
} else if (args[0].equalsIgnoreCase("dontbugme")) {
|
||||
boolean newValue = !plugin.getConfig().getBoolean("checkforupdates", true);
|
||||
plugin.getConfig().set("checkforupdates", newValue);
|
||||
plugin.saveConfig();
|
||||
sender.sendMessage(color("&6We will " + (newValue ? "&anotify you about updates." : "&cnot tell you about updates.")));
|
||||
} else if (args[0].equalsIgnoreCase("autoteam")) {
|
||||
boolean newValue = !plugin.getConfig().getBoolean("auto-team", true);
|
||||
plugin.getConfig().set("auto-team", newValue);
|
||||
plugin.saveConfig();
|
||||
sender.sendMessage(color("&6We will " + (newValue ? "&aautomatically team players" : "&cno longer auto team players")));
|
||||
sender.sendMessage(color("&6All players will need to re-login for the change to take place."));
|
||||
} else {
|
||||
sendHelp(sender);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void sendHelp(CommandSender sender){
|
||||
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
|
||||
sender.sendMessage(color("&6Commands:"));
|
||||
sender.sendMessage(color("&2/viaversion list &7- &6Shows lists of all 1.9 clients and 1.8 clients."));
|
||||
sender.sendMessage(color("&2/viaversion debug &7- &6Toggle debug mode"));
|
||||
sender.sendMessage(color("&2/viaversion autoteam &7- &6Toggle automatically teaming to prevent colliding."));
|
||||
sender.sendMessage(color("&2/viaversion dontbugme &7- &6Toggle checking for updates."));
|
||||
}
|
||||
|
||||
public String color(String string) {
|
||||
return string.replace("&", "§");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class AutoTeamSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "autoteam";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Toggle automatically teaming to prevent colliding.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
boolean newValue = !ViaVersion.getConfig().isAutoTeam();
|
||||
plugin.getConfig().set("auto-team", newValue);
|
||||
plugin.saveConfig();
|
||||
sendMessage(sender, "&6We will %s", (newValue ? "&aautomatically team players" : "&cno longer auto team players"));
|
||||
sendMessage(sender, "&6All players will need to re-login for the change to take place.");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class DebugSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "debug";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Toggle debug mode";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
plugin.setDebug(!plugin.isDebug());
|
||||
sendMessage(sender, "&6Debug mode is now %s", (plugin.isDebug() ? "&aenabled" : "&cdisabled"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import io.netty.util.ResourceLeakDetector;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class DisplayLeaksSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "displayleaks";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Try to hunt memory leaks!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
if (ResourceLeakDetector.getLevel() != ResourceLeakDetector.Level.ADVANCED)
|
||||
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
|
||||
else
|
||||
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
|
||||
|
||||
sendMessage(sender, "&6Leak detector is now %s", (ResourceLeakDetector.getLevel() == ResourceLeakDetector.Level.ADVANCED ? "&aenabled" : "&cdisabled"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class DontBugMeSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "dontbugme";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Toggle checking for updates";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
boolean newValue = !ViaVersion.getConfig().isCheckForUpdates();
|
||||
plugin.getConfig().set("checkforupdates", newValue);
|
||||
plugin.saveConfig();
|
||||
sendMessage(sender, "&6We will %snotify you about updates.", (newValue ? "&a" : "&cnot "));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.commands.ViaCommandHandler;
|
||||
|
||||
public class HelpSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "help";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "You are looking at it right now!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
((ViaCommandHandler) plugin.getCommandHandler()).showHelp(sender);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ListSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Shows lists of the versions from logged in players";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
Map<ProtocolVersion, Set<String>> playerVersions = new TreeMap<>(new Comparator<ProtocolVersion>() {
|
||||
@Override
|
||||
public int compare(ProtocolVersion o1, ProtocolVersion o2) {
|
||||
return ProtocolVersion.getIndex(o2) - ProtocolVersion.getIndex(o1);
|
||||
}
|
||||
});
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
int playerVersion = ViaVersion.getInstance().getPlayerVersion(p);
|
||||
ProtocolVersion key = ProtocolVersion.getProtocol(playerVersion);
|
||||
if (!playerVersions.containsKey(key))
|
||||
playerVersions.put(key, new HashSet<String>());
|
||||
playerVersions.get(key).add(p.getName());
|
||||
}
|
||||
|
||||
for (Map.Entry<ProtocolVersion, Set<String>> entry : playerVersions.entrySet())
|
||||
sendMessage(sender, "&8[&6%s&8]: &b%s", entry.getKey().getName(), entry.getValue());
|
||||
|
||||
playerVersions.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class PPSSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "pps";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Shows the packets per second of online players";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "pps";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
Map<Integer, Set<String>> playerVersions = new HashMap<>();
|
||||
int totalPackets = 0;
|
||||
int clients = 0;
|
||||
long max = 0;
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (!ViaVersion.getInstance().isPorted(p))
|
||||
continue;
|
||||
int playerVersion = ViaVersion.getInstance().getPlayerVersion(p);
|
||||
if (!playerVersions.containsKey(playerVersion))
|
||||
playerVersions.put(playerVersion, new HashSet<String>());
|
||||
UserConnection uc = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(p);
|
||||
if (uc.getPacketsPerSecond() > -1) {
|
||||
playerVersions.get(playerVersion).add(p.getName() + " (" + uc.getPacketsPerSecond() + " PPS)");
|
||||
totalPackets += uc.getPacketsPerSecond();
|
||||
if (uc.getPacketsPerSecond() > max) {
|
||||
max = uc.getPacketsPerSecond();
|
||||
}
|
||||
clients++;
|
||||
}
|
||||
}
|
||||
Map<Integer, Set<String>> sorted = new TreeMap<>(playerVersions);
|
||||
sendMessage(sender, "&4Live Packets Per Second");
|
||||
if (clients > 1) {
|
||||
sendMessage(sender, "&cAverage: &f" + (totalPackets / clients));
|
||||
sendMessage(sender, "&cHighest: &f" + max);
|
||||
}
|
||||
if (clients == 0) {
|
||||
sendMessage(sender, "&cNo clients to display.");
|
||||
}
|
||||
for (Map.Entry<Integer, Set<String>> entry : sorted.entrySet())
|
||||
sendMessage(sender, "&8[&6%s&8]: &b%s", ProtocolVersion.getProtocol(entry.getKey()).getName(), entry.getValue());
|
||||
sorted.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package us.myles.ViaVersion.commands.defaultsubs;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.command.ViaSubCommand;
|
||||
|
||||
public class ReloadSubCmd extends ViaSubCommand {
|
||||
@Override
|
||||
public String name() {
|
||||
return "reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Reload the config from the disk";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String[] args) {
|
||||
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
|
||||
|
||||
plugin.getConf().generateConfig();
|
||||
sendMessage(sender, "&6Configuration successfully reloaded! Some features may need a restart.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package us.myles.ViaVersion;
|
||||
package us.myles.ViaVersion.exception;
|
||||
|
||||
public class CancelException extends Exception {
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package us.myles.ViaVersion.exception;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class InformativeException extends Exception {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
int sources = 0;
|
||||
|
||||
public InformativeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public InformativeException set(String key, Object value) {
|
||||
info.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public InformativeException addSource(Class<?> sourceClazz) {
|
||||
return set("Source #" + sources++, getSource(sourceClazz));
|
||||
}
|
||||
|
||||
private String getSource(Class<?> sourceClazz) {
|
||||
if (sourceClazz.isAnonymousClass()) {
|
||||
return sourceClazz.getName() + " (Anonymous)";
|
||||
} else {
|
||||
return sourceClazz.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Please post this error to http://github.com/MylesIsCool/ViaVersion/issues\n{");
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Object> entry : info.entrySet()) {
|
||||
builder.append((i == 0 ? "" : ", ") + entry.getKey() + ": " + entry.getValue().toString());
|
||||
i++;
|
||||
}
|
||||
builder.append("}\nActual Error: ");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
// Don't record this stack
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public class ViaChunkHandler extends MessageToMessageEncoder {
|
||||
private final ConnectionInfo info;
|
||||
|
||||
public ViaChunkHandler(ConnectionInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object o, List list) throws Exception {
|
||||
// Split chunk bulk packet up in to single chunk packets before it reached the encoder.
|
||||
// This will prevent issues with several plugins and other protocol handlers due to the chunk being sent twice.
|
||||
// It also sends the chunk in the right order possible resolving some issues with added chunk/block/entity data.
|
||||
if (!(o instanceof ByteBuf)) {
|
||||
info.setLastPacket(o);
|
||||
/* This transformer is more for fixing issues which we find hard at packet level :) */
|
||||
if(o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk") && info.isActive()) {
|
||||
list.addAll(info.getChunkManager().transformMapChunkBulk(o));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
list.add(o);
|
||||
}
|
||||
}
|
||||
@@ -3,57 +3,90 @@ package us.myles.ViaVersion.handlers;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.transformers.IncomingTransformer;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.exception.CancelException;
|
||||
import us.myles.ViaVersion.packets.Direction;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
import us.myles.ViaVersion.util.PipelineUtil;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public class ViaDecodeHandler extends ByteToMessageDecoder {
|
||||
|
||||
private final IncomingTransformer incomingTransformer;
|
||||
private final ByteToMessageDecoder minecraftDecoder;
|
||||
private final ConnectionInfo info;
|
||||
private final UserConnection info;
|
||||
public static int PASSTHROUGH_ID = 1000;
|
||||
|
||||
public ViaDecodeHandler(ConnectionInfo info, ByteToMessageDecoder minecraftDecoder) {
|
||||
public ViaDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
|
||||
this.info = info;
|
||||
this.minecraftDecoder = minecraftDecoder;
|
||||
this.incomingTransformer = new IncomingTransformer(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
|
||||
// use transformers
|
||||
if (bytebuf.readableBytes() > 0) {
|
||||
// Ignore if pending disconnect
|
||||
if (info.isPendingDisconnect()) {
|
||||
return;
|
||||
}
|
||||
// Increment received
|
||||
boolean second = info.incrementReceived();
|
||||
// Check PPS
|
||||
if (second) {
|
||||
if (((ViaVersionPlugin) ViaVersion.getInstance()).handlePPS(info))
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.isActive()) {
|
||||
int id = PacketUtil.readVarInt(bytebuf);
|
||||
// Handle ID
|
||||
int id = Type.VAR_INT.read(bytebuf);
|
||||
// Transform
|
||||
ByteBuf newPacket = ctx.alloc().buffer();
|
||||
try {
|
||||
incomingTransformer.transform(id, bytebuf, newPacket);
|
||||
if (id == ViaDecodeHandler.PASSTHROUGH_ID) {
|
||||
newPacket.writeBytes(bytebuf);
|
||||
} else {
|
||||
PacketWrapper wrapper = new PacketWrapper(id, bytebuf, info);
|
||||
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
|
||||
protInfo.getPipeline().transform(Direction.INCOMING, protInfo.getState(), wrapper);
|
||||
wrapper.writeToBuffer(newPacket);
|
||||
}
|
||||
|
||||
bytebuf.clear();
|
||||
bytebuf = newPacket;
|
||||
} catch (Exception e) {
|
||||
// Clear Buffer
|
||||
bytebuf.clear();
|
||||
// Release Packet, be free!
|
||||
newPacket.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// call minecraft decoder
|
||||
try {
|
||||
list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
|
||||
list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof Exception) {
|
||||
throw (Exception) e.getCause();
|
||||
}
|
||||
} finally {
|
||||
if (info.isActive()) {
|
||||
bytebuf.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (PacketUtil.containsCause(cause, CancelException.class)) return;
|
||||
if (PipelineUtil.containsCause(cause, CancelException.class)) return;
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
}
|
||||
@@ -3,32 +3,40 @@ package us.myles.ViaVersion.handlers;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.CancelException;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
import us.myles.ViaVersion.api.PacketWrapper;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.type.Type;
|
||||
import us.myles.ViaVersion.exception.CancelException;
|
||||
import us.myles.ViaVersion.packets.Direction;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
import us.myles.ViaVersion.util.PipelineUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class ViaEncodeHandler extends MessageToByteEncoder {
|
||||
private final ConnectionInfo info;
|
||||
private final UserConnection info;
|
||||
private final MessageToByteEncoder minecraftEncoder;
|
||||
private final OutgoingTransformer outgoingTransformer;
|
||||
|
||||
public ViaEncodeHandler(ConnectionInfo info, MessageToByteEncoder minecraftEncoder) {
|
||||
public ViaEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
|
||||
this.info = info;
|
||||
this.minecraftEncoder = minecraftEncoder;
|
||||
this.outgoingTransformer = new OutgoingTransformer(info);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception {
|
||||
if (ViaVersion.getInstance().isCompatSpigotBuild()) {
|
||||
Field ver = minecraftEncoder.getClass().getDeclaredField("version");
|
||||
ver.setAccessible(true);
|
||||
ver.set(minecraftEncoder, ver.get(this));
|
||||
}
|
||||
// handle the packet type
|
||||
if (!(o instanceof ByteBuf)) {
|
||||
// call minecraft encoder
|
||||
try {
|
||||
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
|
||||
PipelineUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof Exception) {
|
||||
throw (Exception) e.getCause();
|
||||
@@ -38,13 +46,20 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
|
||||
if (bytebuf.readableBytes() == 0) {
|
||||
throw new CancelException();
|
||||
}
|
||||
// Increment sent
|
||||
info.incrementSent();
|
||||
if (info.isActive()) {
|
||||
int id = PacketUtil.readVarInt(bytebuf);
|
||||
// Handle ID
|
||||
int id = Type.VAR_INT.read(bytebuf);
|
||||
// Transform
|
||||
ByteBuf oldPacket = bytebuf.copy();
|
||||
bytebuf.clear();
|
||||
|
||||
try {
|
||||
outgoingTransformer.transform(id, oldPacket, bytebuf);
|
||||
PacketWrapper wrapper = new PacketWrapper(id, oldPacket, info);
|
||||
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
|
||||
protInfo.getPipeline().transform(Direction.OUTGOING, protInfo.getState(), wrapper);
|
||||
wrapper.writeToBuffer(bytebuf);
|
||||
} catch (Exception e) {
|
||||
bytebuf.clear();
|
||||
throw e;
|
||||
@@ -56,7 +71,7 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (PacketUtil.containsCause(cause, CancelException.class)) return;
|
||||
if (PipelineUtil.containsCause(cause, CancelException.class)) return;
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package us.myles.ViaVersion.handlers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ViaPacketHandler extends MessageToMessageEncoder {
|
||||
private final UserConnection info;
|
||||
|
||||
public ViaPacketHandler(UserConnection info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object o, List list) throws Exception {
|
||||
// Split chunks bulk packet up in to single chunks packets before it reached the encoder.
|
||||
// This will prevent issues with several plugins and other protocol handlers due to the chunks being sent twice.
|
||||
// It also sends the chunks in the right order possible resolving some issues with added chunks/block/entity data.
|
||||
if (!(o instanceof ByteBuf)) {
|
||||
info.setLastPacket(o);
|
||||
/* This transformer is more for fixing issues which we find hard at packet level :) */
|
||||
if (info.isActive()) {
|
||||
if (info.get(ProtocolInfo.class).getPipeline().filter(o, list)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.add(o);
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,20 @@ import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import us.myles.ViaVersion.ConnectionInfo;
|
||||
import us.myles.ViaVersion.api.data.UserConnection;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolPipeline;
|
||||
import us.myles.ViaVersion.classgenerator.ClassGenerator;
|
||||
import us.myles.ViaVersion.classgenerator.HandlerConstructor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private final ChannelInitializer<SocketChannel> oldInit;
|
||||
private final ChannelInitializer<SocketChannel> original;
|
||||
private Method method;
|
||||
|
||||
public ViaVersionInitializer(ChannelInitializer<SocketChannel> oldInit) {
|
||||
this.oldInit = oldInit;
|
||||
this.original = oldInit;
|
||||
try {
|
||||
this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
||||
this.method.setAccessible(true);
|
||||
@@ -24,18 +27,26 @@ public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
|
||||
}
|
||||
}
|
||||
|
||||
public ChannelInitializer<SocketChannel> getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
ConnectionInfo info = new ConnectionInfo(socketChannel);
|
||||
UserConnection info = new UserConnection(socketChannel);
|
||||
// init protocol
|
||||
new ProtocolPipeline(info);
|
||||
// Add originals
|
||||
this.method.invoke(this.oldInit, socketChannel);
|
||||
this.method.invoke(this.original, socketChannel);
|
||||
|
||||
HandlerConstructor constructor = ClassGenerator.getConstructor();
|
||||
// Add our transformers
|
||||
ViaEncodeHandler encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder"));
|
||||
ViaDecodeHandler decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder"));
|
||||
ViaChunkHandler chunkHandler = new ViaChunkHandler(info);
|
||||
MessageToByteEncoder encoder = constructor.newEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder"));
|
||||
ByteToMessageDecoder decoder = constructor.newDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder"));
|
||||
ViaPacketHandler chunkHandler = new ViaPacketHandler(info);
|
||||
|
||||
socketChannel.pipeline().replace("encoder", "encoder", encoder);
|
||||
socketChannel.pipeline().replace("decoder", "decoder", decoder);
|
||||
socketChannel.pipeline().addAfter("packet_handler", "viaversion_chunk_handler", chunkHandler);
|
||||
socketChannel.pipeline().addAfter("packet_handler", "viaversion_packet_handler", chunkHandler);
|
||||
}
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
package us.myles.ViaVersion.metadata;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.util.EulerAngle;
|
||||
import org.bukkit.util.Vector;
|
||||
import us.myles.ViaVersion.ViaVersionPlugin;
|
||||
import us.myles.ViaVersion.api.ViaVersion;
|
||||
import us.myles.ViaVersion.slot.ItemSlotRewriter;
|
||||
import us.myles.ViaVersion.slot.ItemSlotRewriter.ItemStack;
|
||||
import us.myles.ViaVersion.transformers.OutgoingTransformer;
|
||||
import us.myles.ViaVersion.util.PacketUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MetadataRewriter {
|
||||
|
||||
public static void writeMetadata1_9(EntityType type, List<Entry> list, ByteBuf output) {
|
||||
short id = -1;
|
||||
int data = -1;
|
||||
for (Entry entry : list) {
|
||||
MetaIndex metaIndex = entry.index;
|
||||
try {
|
||||
if (metaIndex.getNewType() != NewType.Discontinued) {
|
||||
if (metaIndex.getNewType() != NewType.BlockID || id != -1 && data == -1 || id == -1 && data != -1) { // block ID is only written if we have both parts
|
||||
output.writeByte(metaIndex.getNewIndex());
|
||||
output.writeByte(metaIndex.getNewType().getTypeID());
|
||||
}
|
||||
Object value = entry.value;
|
||||
switch (metaIndex.getNewType()) {
|
||||
case Byte:
|
||||
// convert from int, byte
|
||||
if (metaIndex.getOldType() == Type.Byte) {
|
||||
output.writeByte((Byte) value);
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Int) {
|
||||
output.writeByte(((Integer) value).byteValue());
|
||||
}
|
||||
// After writing the last one
|
||||
if (metaIndex == MetaIndex.ENTITY_STATUS && type == EntityType.PLAYER) {
|
||||
output.writeByte(MetaIndex.PLAYER_HAND.getNewIndex());
|
||||
output.writeByte(MetaIndex.PLAYER_HAND.getNewType().getTypeID());
|
||||
if ((((Byte) value) & 0x10) == 0x10) { // Player eating/aiming/drinking
|
||||
output.writeByte(1); // Using main hand
|
||||
} else {
|
||||
output.writeByte(0); // Not using any hand to stop animation
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OptUUID:
|
||||
String owner = (String) value;
|
||||
UUID toWrite = null;
|
||||
if (owner.length() != 0) {
|
||||
try {
|
||||
toWrite = UUID.fromString(owner);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
output.writeBoolean(toWrite != null);
|
||||
if (toWrite != null)
|
||||
PacketUtil.writeUUID(toWrite, output);
|
||||
break;
|
||||
case BlockID:
|
||||
// if we have both sources :))
|
||||
if (metaIndex.getOldType() == Type.Byte) {
|
||||
data = (Byte) value;
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Short) {
|
||||
id = (Short) value;
|
||||
}
|
||||
if (id != -1 && data != -1) {
|
||||
int combined = id << 4 | data;
|
||||
data = -1;
|
||||
id = -1;
|
||||
PacketUtil.writeVarInt(combined, output);
|
||||
}
|
||||
break;
|
||||
case VarInt:
|
||||
// convert from int, short, byte
|
||||
if (metaIndex.getOldType() == Type.Byte) {
|
||||
PacketUtil.writeVarInt(((Byte) value).intValue(), output);
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Short) {
|
||||
PacketUtil.writeVarInt(((Short) value).intValue(), output);
|
||||
}
|
||||
if (metaIndex.getOldType() == Type.Int) {
|
||||
PacketUtil.writeVarInt((Integer) value, output);
|
||||
}
|
||||
break;
|
||||
case Float:
|
||||
output.writeFloat((Float) value);
|
||||
break;
|
||||
case String:
|
||||
PacketUtil.writeString((String) value, output);
|
||||
break;
|
||||
case Boolean:
|
||||
if (metaIndex == MetaIndex.AGEABLE_AGE)
|
||||
output.writeBoolean((Byte) value < 0);
|
||||
else
|
||||
output.writeBoolean((Byte) value != 0);
|
||||
break;
|
||||
case Slot:
|
||||
ItemStack item = (ItemStack) value;
|
||||
ItemSlotRewriter.fixIdsFrom1_8To1_9(item);
|
||||
ItemSlotRewriter.writeItemStack(item, output);
|
||||
break;
|
||||
case Position:
|
||||
Vector vector = (Vector) value;
|
||||
output.writeInt((int) vector.getX());
|
||||
output.writeInt((int) vector.getY());
|
||||
output.writeInt((int) vector.getZ());
|
||||
break;
|
||||
case Vector3F:
|
||||
EulerAngle angle = (EulerAngle) value;
|
||||
output.writeFloat((float) angle.getX());
|
||||
output.writeFloat((float) angle.getY());
|
||||
output.writeFloat((float) angle.getZ());
|
||||
break;
|
||||
case Chat:
|
||||
PacketUtil.writeString(OutgoingTransformer.fixJson((String) value), output);
|
||||
break;
|
||||
default:
|
||||
System.out.println("[Out] Unhandled MetaDataType: " + metaIndex.getNewType());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (!((ViaVersionPlugin) ViaVersion.getInstance()).isSuppressMetadataErrors()) {
|
||||
System.out.println("INCLUDE THIS IN YOUR ERROR LOG!");
|
||||
if (type != null)
|
||||
System.out.println("An error occurred with entity meta data for " + type + " OldID: " + entry.oldID);
|
||||
else
|
||||
System.out.println("An error occurred with entity meta data for UNKNOWN_ENTITY OldID: " + entry.oldID);
|
||||
if (metaIndex != null) {
|
||||
System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex());
|
||||
System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType());
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
output.writeByte(255);
|
||||
}
|
||||
|
||||
public static List<Entry> readMetadata1_8(EntityType entityType, ByteBuf buf) {
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
byte item;
|
||||
while ((item = buf.readByte()) != 127) {
|
||||
Type type = Type.byId((item & 0xE0) >> 5);
|
||||
int id = item & 0x1F;
|
||||
MetaIndex index = MetaIndex.getIndex(entityType, id);
|
||||
switch (type) {
|
||||
case Byte:
|
||||
entries.add(new Entry(index, buf.readByte(), id));
|
||||
break;
|
||||
case Short:
|
||||
entries.add(new Entry(index, buf.readShort(), id));
|
||||
break;
|
||||
case Int:
|
||||
entries.add(new Entry(index, buf.readInt(), id));
|
||||
break;
|
||||
case Float:
|
||||
entries.add(new Entry(index, buf.readFloat(), id));
|
||||
break;
|
||||
case String:
|
||||
entries.add(new Entry(index, PacketUtil.readString(buf), id));
|
||||
break;
|
||||
case Slot: {
|
||||
try {
|
||||
entries.add(new Entry(index, ItemSlotRewriter.readItemStack(buf), id));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Position: {
|
||||
int x = buf.readInt();
|
||||
int y = buf.readInt();
|
||||
int z = buf.readInt();
|
||||
entries.add(new Entry(index, new Vector(x, y, z), id));
|
||||
break;
|
||||
}
|
||||
case Rotation: {
|
||||
float x = buf.readFloat();
|
||||
float y = buf.readFloat();
|
||||
float z = buf.readFloat();
|
||||
entries.add(new Entry(index, new EulerAngle(x, y, z), id));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
System.out.println("[Out] Unhandled MetaDataType: " + type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static final class Entry {
|
||||
|
||||
private final int oldID;
|
||||
private MetaIndex index;
|
||||
private Object value;
|
||||
|
||||
public Entry(MetaIndex index, Object value, int id) {
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.oldID = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package us.myles.ViaVersion.metadata;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum NewType {
|
||||
Byte(0),
|
||||
VarInt(1),
|
||||
Float(2),
|
||||
String(3),
|
||||
Chat(4),
|
||||
Slot(5),
|
||||
Boolean(6),
|
||||
Vector3F(7),
|
||||
Position(8),
|
||||
OptPosition(9),
|
||||
Direction(10),
|
||||
OptUUID(11),
|
||||
BlockID(12),
|
||||
Discontinued(99);
|
||||
|
||||
private final int typeID;
|
||||
|
||||
}
|
||||
@@ -2,139 +2,153 @@ package us.myles.ViaVersion.packets;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
@Deprecated
|
||||
public enum PacketType {
|
||||
/* Handshake serverbound */
|
||||
HANDSHAKE(State.HANDSHAKE, Direction.INCOMING, 0x00),
|
||||
HANDSHAKE(State.HANDSHAKE, Direction.INCOMING, 0x00), // Mapped
|
||||
/* Login serverbound */
|
||||
LOGIN_START(State.LOGIN, Direction.INCOMING, 0x00),
|
||||
LOGIN_ENCRYPTION_RESPONSE(State.LOGIN, Direction.INCOMING, 0x01),
|
||||
LOGIN_START(State.LOGIN, Direction.INCOMING, 0x00), // Mapped
|
||||
LOGIN_ENCRYPTION_RESPONSE(State.LOGIN, Direction.INCOMING, 0x01), // Mapped
|
||||
/* Login clientbound */
|
||||
LOGIN_DISCONNECT(State.LOGIN, Direction.OUTGOING, 0x00),
|
||||
LOGIN_ENCRYPTION_REQUEST(State.LOGIN, Direction.OUTGOING, 0x01),
|
||||
LOGIN_SUCCESS(State.LOGIN, Direction.OUTGOING, 0x02),
|
||||
LOGIN_SETCOMPRESSION(State.LOGIN, Direction.OUTGOING, 0x03),
|
||||
LOGIN_DISCONNECT(State.LOGIN, Direction.OUTGOING, 0x00), // Mapped
|
||||
LOGIN_ENCRYPTION_REQUEST(State.LOGIN, Direction.OUTGOING, 0x01), // Mapped
|
||||
LOGIN_SUCCESS(State.LOGIN, Direction.OUTGOING, 0x02), // Mapped
|
||||
LOGIN_SETCOMPRESSION(State.LOGIN, Direction.OUTGOING, 0x03), // Mapped
|
||||
|
||||
/* Status serverbound */
|
||||
STATUS_REQUEST(State.STATUS, Direction.INCOMING, 0x00),
|
||||
STATUS_PING(State.STATUS, Direction.INCOMING, 0x01),
|
||||
STATUS_REQUEST(State.STATUS, Direction.INCOMING, 0x00), // Mapped
|
||||
STATUS_PING(State.STATUS, Direction.INCOMING, 0x01), // Mapped
|
||||
/* Status clientbound */
|
||||
STATUS_RESPONSE(State.STATUS, Direction.OUTGOING, 0x00),
|
||||
STATUS_PONG(State.STATUS, Direction.OUTGOING, 0x01),
|
||||
/* Play serverbound */
|
||||
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00),
|
||||
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01),
|
||||
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
|
||||
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03),
|
||||
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04),
|
||||
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05),
|
||||
PLAY_ENCHANT_ITEM(State.PLAY, Direction.INCOMING, 0x11, 0x06),
|
||||
PLAY_CLICK_WINDOW(State.PLAY, Direction.INCOMING, 0x0E, 0x07),
|
||||
PLAY_CLOSE_WINDOW_REQUEST(State.PLAY, Direction.INCOMING, 0x0D, 0x08),
|
||||
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00), // Mapped
|
||||
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01), // Mapped
|
||||
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02), // Mapped
|
||||
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03), // Mapped
|
||||
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04), // Mapped
|
||||
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05), // Mapped
|
||||
PLAY_ENCHANT_ITEM(State.PLAY, Direction.INCOMING, 0x11, 0x06), // Mapped
|
||||
PLAY_CLICK_WINDOW(State.PLAY, Direction.INCOMING, 0x0E, 0x07), // Mapped
|
||||
PLAY_CLOSE_WINDOW_REQUEST(State.PLAY, Direction.INCOMING, 0x0D, 0x08), // Mapped
|
||||
PLAY_PLUGIN_MESSAGE_REQUEST(State.PLAY, Direction.INCOMING, 0x17, 0x09),
|
||||
PLAY_USE_ENTITY(State.PLAY, Direction.INCOMING, 0x02, 0x0A),
|
||||
PLAY_KEEP_ALIVE_REQUEST(State.PLAY, Direction.INCOMING, 0x00, 0x0B),
|
||||
PLAY_PLAYER_POSITION_REQUEST(State.PLAY, Direction.INCOMING, 0x04, 0x0C),
|
||||
PLAY_PLAYER_POSITION_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x06, 0x0D),
|
||||
PLAY_PLAYER_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x05, 0x0E),
|
||||
PLAY_PLAYER(State.PLAY, Direction.INCOMING, 0x03, 0x0F),
|
||||
PLAY_VEHICLE_MOVE_REQUEST(State.PLAY, Direction.INCOMING, -1, 0x10),
|
||||
PLAY_STEER_BOAT(State.PLAY, Direction.INCOMING, -1, 0x11),
|
||||
PLAY_PLAYER_ABILITIES_REQUEST(State.PLAY, Direction.INCOMING, 0x13, 0x12),
|
||||
PLAY_PLAYER_DIGGING(State.PLAY, Direction.INCOMING, 0x07, 0x13),
|
||||
PLAY_ENTITY_ACTION(State.PLAY, Direction.INCOMING, 0x0B, 0x14),
|
||||
PLAY_STEER_VEHICLE(State.PLAY, Direction.INCOMING, 0x0C, 0x15),
|
||||
PLAY_RESOURCE_PACK_STATUS(State.PLAY, Direction.INCOMING, 0x19, 0x16),
|
||||
PLAY_HELD_ITEM_CHANGE_REQUEST(State.PLAY, Direction.INCOMING, 0x09, 0x17),
|
||||
PLAY_CREATIVE_INVENTORY_ACTION(State.PLAY, Direction.INCOMING, 0x10, 0x18),
|
||||
PLAY_UPDATE_SIGN_REQUEST(State.PLAY, Direction.INCOMING, 0x12, 0x19),
|
||||
PLAY_ANIMATION_REQUEST(State.PLAY, Direction.INCOMING, 0x0A, 0x1A),
|
||||
PLAY_SPECTATE(State.PLAY, Direction.INCOMING, 0x18, 0x1B),
|
||||
PLAY_PLAYER_BLOCK_PLACEMENT(State.PLAY, Direction.INCOMING, 0x08, 0x1C),
|
||||
PLAY_USE_ITEM(State.PLAY, Direction.INCOMING, -1, 0x1D),
|
||||
PLAY_USE_ENTITY(State.PLAY, Direction.INCOMING, 0x02, 0x0A), // Mapped
|
||||
PLAY_KEEP_ALIVE_REQUEST(State.PLAY, Direction.INCOMING, 0x00, 0x0B), // Mapped
|
||||
PLAY_PLAYER_POSITION_REQUEST(State.PLAY, Direction.INCOMING, 0x04, 0x0C), // Mapped
|
||||
PLAY_PLAYER_POSITION_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x06, 0x0D), // Mapped
|
||||
PLAY_PLAYER_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x05, 0x0E), // Mapped
|
||||
PLAY_PLAYER(State.PLAY, Direction.INCOMING, 0x03, 0x0F), // Mapped
|
||||
PLAY_VEHICLE_MOVE_REQUEST(State.PLAY, Direction.INCOMING, -1, 0x10), // Mapped
|
||||
PLAY_STEER_BOAT(State.PLAY, Direction.INCOMING, -1, 0x11), // Mapped
|
||||
PLAY_PLAYER_ABILITIES_REQUEST(State.PLAY, Direction.INCOMING, 0x13, 0x12), // Mapped
|
||||
PLAY_PLAYER_DIGGING(State.PLAY, Direction.INCOMING, 0x07, 0x13), // Mapped
|
||||
PLAY_ENTITY_ACTION(State.PLAY, Direction.INCOMING, 0x0B, 0x14), // Mapped
|
||||
PLAY_STEER_VEHICLE(State.PLAY, Direction.INCOMING, 0x0C, 0x15), // Mapped
|
||||
|
||||
PLAY_RESOURCE_PACK_STATUS(State.PLAY, Direction.INCOMING, 0x19, 0x16), // Mapped
|
||||
PLAY_HELD_ITEM_CHANGE_REQUEST(State.PLAY, Direction.INCOMING, 0x09, 0x17), // Mapped
|
||||
|
||||
PLAY_CREATIVE_INVENTORY_ACTION(State.PLAY, Direction.INCOMING, 0x10, 0x18), // Mapped
|
||||
PLAY_UPDATE_SIGN_REQUEST(State.PLAY, Direction.INCOMING, 0x12, 0x19), // Mapped
|
||||
PLAY_ANIMATION_REQUEST(State.PLAY, Direction.INCOMING, 0x0A, 0x1A), // Mapped
|
||||
PLAY_SPECTATE(State.PLAY, Direction.INCOMING, 0x18, 0x1B), // Mapped
|
||||
PLAY_PLAYER_BLOCK_PLACEMENT(State.PLAY, Direction.INCOMING, 0x08, 0x1C), // Mapped
|
||||
PLAY_USE_ITEM(State.PLAY, Direction.INCOMING, -1, 0x1D), // Mapped
|
||||
/* Play clientbound */
|
||||
PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00),
|
||||
PLAY_SPAWN_XP_ORB(State.PLAY, Direction.OUTGOING, 0x11, 0x01),
|
||||
PLAY_SPAWN_GLOBAL_ENTITY(State.PLAY, Direction.OUTGOING, 0x2C, 0x02),
|
||||
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03),
|
||||
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04),
|
||||
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05),
|
||||
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06),
|
||||
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07),
|
||||
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08),
|
||||
PLAY_UPDATE_BLOCK_ENTITY(State.PLAY, Direction.OUTGOING, 0x35, 0x09),
|
||||
PLAY_BLOCK_ACTION(State.PLAY, Direction.OUTGOING, 0x24, 0x0A),
|
||||
PLAY_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x23, 0x0B),
|
||||
PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00), // Mapped
|
||||
PLAY_SPAWN_XP_ORB(State.PLAY, Direction.OUTGOING, 0x11, 0x01), // Mapped
|
||||
PLAY_SPAWN_GLOBAL_ENTITY(State.PLAY, Direction.OUTGOING, 0x2C, 0x02), // Mapped
|
||||
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03), // Mapped
|
||||
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04), // Mapped
|
||||
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05), // Mapped
|
||||
|
||||
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06), // Mapped
|
||||
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07), // Mapped
|
||||
|
||||
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08), // Mapped
|
||||
PLAY_UPDATE_BLOCK_ENTITY(State.PLAY, Direction.OUTGOING, 0x35, 0x09), // Mapped
|
||||
PLAY_BLOCK_ACTION(State.PLAY, Direction.OUTGOING, 0x24, 0x0A), // Mapped
|
||||
PLAY_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x23, 0x0B), // Mapped
|
||||
|
||||
PLAY_BOSS_BAR(State.PLAY, Direction.OUTGOING, -1, 0x0C),
|
||||
PLAY_SERVER_DIFFICULTY(State.PLAY, Direction.OUTGOING, 0x41, 0x0D),
|
||||
PLAY_TAB_COMPLETE(State.PLAY, Direction.OUTGOING, 0x3A, 0x0E),
|
||||
PLAY_CHAT_MESSAGE(State.PLAY, Direction.OUTGOING, 0x02, 0x0F),
|
||||
PLAY_MULTI_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x22, 0x10),
|
||||
PLAY_CONFIRM_TRANSACTION(State.PLAY, Direction.OUTGOING, 0x32, 0x11),
|
||||
PLAY_CLOSE_WINDOW(State.PLAY, Direction.OUTGOING, 0x2E, 0x12),
|
||||
PLAY_OPEN_WINDOW(State.PLAY, Direction.OUTGOING, 0x2D, 0x13),
|
||||
PLAY_WINDOW_ITEMS(State.PLAY, Direction.OUTGOING, 0x30, 0x14),
|
||||
PLAY_WINDOW_PROPERTY(State.PLAY, Direction.OUTGOING, 0x31, 0x15),
|
||||
PLAY_SET_SLOT(State.PLAY, Direction.OUTGOING, 0x2F, 0x16),
|
||||
PLAY_SERVER_DIFFICULTY(State.PLAY, Direction.OUTGOING, 0x41, 0x0D), // Mapped
|
||||
PLAY_TAB_COMPLETE(State.PLAY, Direction.OUTGOING, 0x3A, 0x0E), // Mapped
|
||||
PLAY_CHAT_MESSAGE(State.PLAY, Direction.OUTGOING, 0x02, 0x0F), // Mapped
|
||||
PLAY_MULTI_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x22, 0x10), // Mapped
|
||||
PLAY_CONFIRM_TRANSACTION(State.PLAY, Direction.OUTGOING, 0x32, 0x11), // Mapped
|
||||
PLAY_CLOSE_WINDOW(State.PLAY, Direction.OUTGOING, 0x2E, 0x12), // Mapped
|
||||
PLAY_OPEN_WINDOW(State.PLAY, Direction.OUTGOING, 0x2D, 0x13), // Mapped
|
||||
PLAY_WINDOW_ITEMS(State.PLAY, Direction.OUTGOING, 0x30, 0x14), // Mapped
|
||||
PLAY_WINDOW_PROPERTY(State.PLAY, Direction.OUTGOING, 0x31, 0x15), // Mapped
|
||||
PLAY_SET_SLOT(State.PLAY, Direction.OUTGOING, 0x2F, 0x16), // Mapped
|
||||
PLAY_SET_COOLDOWN(State.PLAY, Direction.OUTGOING, -1, 0x17),
|
||||
PLAY_PLUGIN_MESSAGE(State.PLAY, Direction.OUTGOING, 0x3F, 0x18),
|
||||
PLAY_NAMED_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, 0x29, 0x19),
|
||||
PLAY_DISCONNECT(State.PLAY, Direction.OUTGOING, 0x40, 0x1A),
|
||||
PLAY_ENTITY_STATUS(State.PLAY, Direction.OUTGOING, 0x1A, 0x1B),
|
||||
PLAY_EXPLOSION(State.PLAY, Direction.OUTGOING, 0x27, 0x1C),
|
||||
PLAY_PLUGIN_MESSAGE(State.PLAY, Direction.OUTGOING, 0x3F, 0x18), // Mapped
|
||||
PLAY_NAMED_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, 0x29, 0x19), // Mapped
|
||||
PLAY_DISCONNECT(State.PLAY, Direction.OUTGOING, 0x40, 0x1A), // Mapped
|
||||
PLAY_ENTITY_STATUS(State.PLAY, Direction.OUTGOING, 0x1A, 0x1B), // Mapped
|
||||
PLAY_EXPLOSION(State.PLAY, Direction.OUTGOING, 0x27, 0x1C), // Mapped
|
||||
PLAY_UNLOAD_CHUNK(State.PLAY, Direction.OUTGOING, -1, 0x1D),
|
||||
PLAY_CHANGE_GAME_STATE(State.PLAY, Direction.OUTGOING, 0x2B, 0x1E),
|
||||
PLAY_KEEP_ALIVE(State.PLAY, Direction.OUTGOING, 0x00, 0x1F),
|
||||
PLAY_CHUNK_DATA(State.PLAY, Direction.OUTGOING, 0x21, 0x20),
|
||||
PLAY_EFFECT(State.PLAY, Direction.OUTGOING, 0x28, 0x21),
|
||||
PLAY_PARTICLE(State.PLAY, Direction.OUTGOING, 0x2A, 0x22),
|
||||
PLAY_JOIN_GAME(State.PLAY, Direction.OUTGOING, 0x01, 0x23),
|
||||
PLAY_MAP(State.PLAY, Direction.OUTGOING, 0x34, 0x24),
|
||||
PLAY_ENTITY_RELATIVE_MOVE(State.PLAY, Direction.OUTGOING, 0x15, 0x25),
|
||||
PLAY_ENTITY_LOOK_MOVE(State.PLAY, Direction.OUTGOING, 0x17, 0x26),
|
||||
PLAY_ENTITY_LOOK(State.PLAY, Direction.OUTGOING, 0x16, 0x27),
|
||||
PLAY_ENTITY(State.PLAY, Direction.OUTGOING, 0x14, 0x28),
|
||||
PLAY_KEEP_ALIVE(State.PLAY, Direction.OUTGOING, 0x00, 0x1F), // Mapped
|
||||
PLAY_CHUNK_DATA(State.PLAY, Direction.OUTGOING, 0x21, 0x20), // Mapped
|
||||
PLAY_EFFECT(State.PLAY, Direction.OUTGOING, 0x28, 0x21), // Mapped
|
||||
PLAY_PARTICLE(State.PLAY, Direction.OUTGOING, 0x2A, 0x22), // Mapped
|
||||
PLAY_JOIN_GAME(State.PLAY, Direction.OUTGOING, 0x01, 0x23), // Mapped
|
||||
PLAY_MAP(State.PLAY, Direction.OUTGOING, 0x34, 0x24), // Mapped
|
||||
PLAY_ENTITY_RELATIVE_MOVE(State.PLAY, Direction.OUTGOING, 0x15, 0x25), // Mapped
|
||||
PLAY_ENTITY_LOOK_MOVE(State.PLAY, Direction.OUTGOING, 0x17, 0x26), // Mapped
|
||||
PLAY_ENTITY_LOOK(State.PLAY, Direction.OUTGOING, 0x16, 0x27), // Mapped
|
||||
PLAY_ENTITY(State.PLAY, Direction.OUTGOING, 0x14, 0x28), // Mapped
|
||||
PLAY_VEHICLE_MOVE(State.PLAY, Direction.OUTGOING, -1, 0x29),
|
||||
PLAY_OPEN_SIGN_EDITOR(State.PLAY, Direction.OUTGOING, 0x36, 0x2A),
|
||||
PLAY_PLAYER_ABILITIES(State.PLAY, Direction.OUTGOING, 0x39, 0x2B),
|
||||
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C),
|
||||
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D),
|
||||
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E),
|
||||
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F),
|
||||
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30),
|
||||
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31),
|
||||
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32),
|
||||
PLAY_RESPAWN(State.PLAY, Direction.OUTGOING, 0x07, 0x33),
|
||||
PLAY_ENTITY_HEAD_LOOK(State.PLAY, Direction.OUTGOING, 0x19, 0x34),
|
||||
PLAY_WORLD_BORDER(State.PLAY, Direction.OUTGOING, 0x44, 0x35),
|
||||
PLAY_CAMERA(State.PLAY, Direction.OUTGOING, 0x43, 0x36),
|
||||
PLAY_HELD_ITEM_CHANGE(State.PLAY, Direction.OUTGOING, 0x09, 0x37),
|
||||
PLAY_DISPLAY_SCOREBOARD(State.PLAY, Direction.OUTGOING, 0x3D, 0x38),
|
||||
PLAY_ENTITY_METADATA(State.PLAY, Direction.OUTGOING, 0x1C, 0x39),
|
||||
PLAY_ATTACH_ENTITY(State.PLAY, Direction.OUTGOING, 0x1B, 0x3A),
|
||||
PLAY_ENTITY_VELOCITY(State.PLAY, Direction.OUTGOING, 0x12, 0x3B),
|
||||
PLAY_ENTITY_EQUIPMENT(State.PLAY, Direction.OUTGOING, 0x04, 0x3C),
|
||||
PLAY_SET_XP(State.PLAY, Direction.OUTGOING, 0x1F, 0x3D),
|
||||
PLAY_UPDATE_HEALTH(State.PLAY, Direction.OUTGOING, 0x06, 0x3E),
|
||||
PLAY_SCOREBOARD_OBJ(State.PLAY, Direction.OUTGOING, 0x3B, 0x3F),
|
||||
PLAY_OPEN_SIGN_EDITOR(State.PLAY, Direction.OUTGOING, 0x36, 0x2A), // Mapped
|
||||
PLAY_PLAYER_ABILITIES(State.PLAY, Direction.OUTGOING, 0x39, 0x2B), // Mapped
|
||||
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C), // Mapped
|
||||
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D), // Mapped
|
||||
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E), // Mapped
|
||||
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F), // Mapped
|
||||
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30), // Mapped
|
||||
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31), // Mapped
|
||||
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32), // Mapped
|
||||
PLAY_RESPAWN(State.PLAY, Direction.OUTGOING, 0x07, 0x33), // Mapped
|
||||
PLAY_ENTITY_HEAD_LOOK(State.PLAY, Direction.OUTGOING, 0x19, 0x34), // Mapped
|
||||
PLAY_WORLD_BORDER(State.PLAY, Direction.OUTGOING, 0x44, 0x35), // Mapped
|
||||
PLAY_CAMERA(State.PLAY, Direction.OUTGOING, 0x43, 0x36), // Mapped
|
||||
PLAY_HELD_ITEM_CHANGE(State.PLAY, Direction.OUTGOING, 0x09, 0x37), // Mapped
|
||||
PLAY_DISPLAY_SCOREBOARD(State.PLAY, Direction.OUTGOING, 0x3D, 0x38), // Mapped
|
||||
PLAY_ENTITY_METADATA(State.PLAY, Direction.OUTGOING, 0x1C, 0x39), // Mapped
|
||||
PLAY_ATTACH_ENTITY(State.PLAY, Direction.OUTGOING, 0x1B, 0x3A), // Mapped
|
||||
PLAY_ENTITY_VELOCITY(State.PLAY, Direction.OUTGOING, 0x12, 0x3B), // Mapped
|
||||
PLAY_ENTITY_EQUIPMENT(State.PLAY, Direction.OUTGOING, 0x04, 0x3C), // Mapped
|
||||
PLAY_SET_XP(State.PLAY, Direction.OUTGOING, 0x1F, 0x3D), // Mapped
|
||||
PLAY_UPDATE_HEALTH(State.PLAY, Direction.OUTGOING, 0x06, 0x3E), // Mapped
|
||||
PLAY_SCOREBOARD_OBJ(State.PLAY, Direction.OUTGOING, 0x3B, 0x3F), // Mapped
|
||||
PLAY_SET_PASSENGERS(State.PLAY, Direction.OUTGOING, -1, 0x40),
|
||||
PLAY_TEAM(State.PLAY, Direction.OUTGOING, 0x3E, 0x41),
|
||||
PLAY_UPDATE_SCORE(State.PLAY, Direction.OUTGOING, 0x3C, 0x42),
|
||||
PLAY_SPAWN_POSITION(State.PLAY, Direction.OUTGOING, 0x05, 0x43),
|
||||
PLAY_TIME_UPDATE(State.PLAY, Direction.OUTGOING, 0x03, 0x44),
|
||||
PLAY_TITLE(State.PLAY, Direction.OUTGOING, 0x45, 0x45),
|
||||
PLAY_UPDATE_SIGN(State.PLAY, Direction.OUTGOING, 0x33, 0x46),
|
||||
PLAY_TEAM(State.PLAY, Direction.OUTGOING, 0x3E, 0x41), // Mapped
|
||||
PLAY_UPDATE_SCORE(State.PLAY, Direction.OUTGOING, 0x3C, 0x42), // Mapped
|
||||
PLAY_SPAWN_POSITION(State.PLAY, Direction.OUTGOING, 0x05, 0x43), // Mapped
|
||||
PLAY_TIME_UPDATE(State.PLAY, Direction.OUTGOING, 0x03, 0x44), // Mapped
|
||||
PLAY_TITLE(State.PLAY, Direction.OUTGOING, 0x45, 0x45), // Mapped
|
||||
PLAY_UPDATE_SIGN(State.PLAY, Direction.OUTGOING, 0x33, 0x46), // Mapped
|
||||
PLAY_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, -1, 0x47),
|
||||
PLAY_PLAYER_LIST_HEADER_FOOTER(State.PLAY, Direction.OUTGOING, 0x47, 0x48),
|
||||
PLAY_COLLECT_ITEM(State.PLAY, Direction.OUTGOING, 0x0D, 0x49),
|
||||
PLAY_ENTITY_TELEPORT(State.PLAY, Direction.OUTGOING, 0x18, 0x4A),
|
||||
PLAY_ENTITY_PROPERTIES(State.PLAY, Direction.OUTGOING, 0x20, 0x4B),
|
||||
PLAY_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1D, 0x4C),
|
||||
PLAY_PLAYER_LIST_HEADER_FOOTER(State.PLAY, Direction.OUTGOING, 0x47, 0x48), // Mapped
|
||||
PLAY_COLLECT_ITEM(State.PLAY, Direction.OUTGOING, 0x0D, 0x49), // Mapped
|
||||
PLAY_ENTITY_TELEPORT(State.PLAY, Direction.OUTGOING, 0x18, 0x4A), // Mapped
|
||||
PLAY_ENTITY_PROPERTIES(State.PLAY, Direction.OUTGOING, 0x20, 0x4B), // Mapped
|
||||
PLAY_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1D, 0x4C), // Mapped
|
||||
|
||||
PLAY_MAP_CHUNK_BULK(State.PLAY, Direction.OUTGOING, 0x26, -1),
|
||||
PLAY_SET_COMPRESSION(State.PLAY, Direction.OUTGOING, 0x46, -1),
|
||||
PLAY_UPDATE_ENTITY_NBT(State.PLAY, Direction.OUTGOING, 0x49, -1),;
|
||||
PLAY_UPDATE_ENTITY_NBT(State.PLAY, Direction.OUTGOING, 0x49, -1);
|
||||
|
||||
private static HashMap<Short, PacketType> oldids = new HashMap<>();
|
||||
private static HashMap<Short, PacketType> newids = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (PacketType pt : PacketType.values()) {
|
||||
oldids.put(toShort((short) pt.getPacketID(), (short) pt.getDirection().ordinal(), (short) pt.getState().ordinal()), pt);
|
||||
newids.put(toShort((short) pt.getNewPacketID(), (short) pt.getDirection().ordinal(), (short) pt.getState().ordinal()), pt);
|
||||
}
|
||||
}
|
||||
|
||||
private State state;
|
||||
private Direction direction;
|
||||
@@ -155,6 +169,26 @@ public enum PacketType {
|
||||
this.newPacketID = newPacketID;
|
||||
}
|
||||
|
||||
public static PacketType findNewPacket(State state, Direction direction, int id) {
|
||||
return newids.get(toShort((short) id, (short) direction.ordinal(), (short) state.ordinal()));
|
||||
}
|
||||
|
||||
public static PacketType findOldPacket(State state, Direction direction, int id) {
|
||||
return oldids.get(toShort((short) id, (short) direction.ordinal(), (short) state.ordinal()));
|
||||
}
|
||||
|
||||
public static PacketType getIncomingPacket(State state, int id) {
|
||||
return findNewPacket(state, Direction.INCOMING, id);
|
||||
}
|
||||
|
||||
public static PacketType getOutgoingPacket(State state, int id) {
|
||||
return findOldPacket(state, Direction.OUTGOING, id);
|
||||
}
|
||||
|
||||
private static short toShort(short id, short direction, short state) {
|
||||
return (short) ((id & 0x00FF) | (direction << 8) & 0x0F00 | (state << 12) & 0xF000);
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
@@ -171,33 +205,4 @@ public enum PacketType {
|
||||
return newPacketID;
|
||||
}
|
||||
|
||||
public static PacketType findNewPacket(State state, Direction direction, int id) {
|
||||
return newids.get(toShort((short) id, (short)direction.ordinal(), (short) state.ordinal()));
|
||||
}
|
||||
|
||||
public static PacketType findOldPacket(State state, Direction direction, int id) {
|
||||
return oldids.get(toShort((short) id, (short)direction.ordinal(), (short) state.ordinal()));
|
||||
}
|
||||
|
||||
public static PacketType getIncomingPacket(State state, int id) {
|
||||
return findNewPacket(state, Direction.INCOMING, id);
|
||||
}
|
||||
|
||||
public static PacketType getOutgoingPacket(State state, int id) {
|
||||
return findOldPacket(state, Direction.OUTGOING, id);
|
||||
}
|
||||
|
||||
private static short toShort(short id, short direction, short state) {
|
||||
return (short) ((id & 0x00FF) | (direction<<8) & 0x0F00 | (state << 12) & 0xF000);
|
||||
}
|
||||
|
||||
private static HashMap<Short, PacketType> oldids = new HashMap<Short, PacketType>();
|
||||
private static HashMap<Short, PacketType> newids = new HashMap<Short, PacketType>();
|
||||
static {
|
||||
for(PacketType pt : PacketType.values()) {
|
||||
oldids.put(toShort((short) pt.getPacketID(), (short)pt.getDirection().ordinal(), (short)pt.getState().ordinal()), pt);
|
||||
newids.put(toShort((short) pt.getNewPacketID(), (short)pt.getDirection().ordinal(), (short)pt.getState().ordinal()), pt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user