Compare commits

...
138 Commits
Author SHA1 Message Date
Myles f91fcb4ea8 Bump version 2016-06-22 21:33:25 +01:00
Myles b3060e0dc8 Start work on 1.9.2 chunk stuff, no where near done bit stuck on detecting skylight. 2016-06-22 21:32:26 +01:00
Myles 808a9a7d82 Small PPS change to allow %pps to be used as a placeholder. 2016-06-22 21:31:27 +01:00
Myles 32c43e6023 Fix chunks not being written and array type being weird. 2016-06-22 21:22:19 +01:00
Matsv 4c9c50f939 WIP: Move the TileEntities writer to the 1.9 transformer. 2016-06-22 22:00:24 +02:00
Matsv ac1b35379e Remove not required tags, and remove tested todo's 2016-06-22 20:38:40 +02:00
Myles b743589d77 This works but lags, only for 1.8 server. 2016-06-22 19:22:38 +01:00
Myles 9dad0a0d5e Fix the arrays 2016-06-22 18:20:42 +01:00
Matsv f5b49bc9ce Experimental implementation for block entities into chunks 2016-06-22 19:07:46 +02:00
Matsv b3612a68b0 Merge branch '1.10.1-chunkrefactor' of https://github.com/MylesIsCool/ViaVersion into 1.10.1 2016-06-22 18:36:42 +02:00
Myles 7385db02c2 Reorganise packages and push initial interface 2016-06-22 17:35:07 +01:00
Matsv 501d2ec3a2 Rewrite block tile entities. Not tested yet 2016-06-22 18:03:27 +02:00
Myles 1217a9b7e2 Make versions ordered by the index inserted. (Seems to work best over alphabetical) 2016-06-19 23:32:41 +01:00
Myles 6740c5c6dc Spring cleaning & make /viaver pps say when no players. 2016-06-19 22:36:15 +01:00
Myles 44e8056fb3 Add more versions & use ProtocolSupport API if not ported. 2016-06-19 22:35:49 +01:00
Myles d1da412cf7 Disable our system when an unknown handshake happens, fixes support for older mc versions. 2016-06-19 22:04:54 +01:00
Myles 6534a5414e Basic compatibility with ProtocolSupport, doesn't seem to work with 1.6.4 due to issues with our Login Handler. 2016-06-19 18:08:49 +01:00
Myles 91344a0b50 Cleanup some of the block placement code. 2016-06-19 18:07:00 +01:00
Myles cdd9ff4469 Back to snapshot 2016-06-15 19:47:10 +01:00
Myles d88b76db85 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-06-15 19:45:28 +01:00
Myles bc1d57853c Bump version 2016-06-15 19:44:25 +01:00
Troy Frew 27d6c12556 Added TacoSpigot to the block place fix. (#437)
TacoSpigot is a fork of PaperSpigot that people use.
2016-06-13 09:29:10 +02:00
Mats a76dd1ba49 Merge pull request #427 from Harry5573/master
Correct hologram Y offset
2016-06-10 15:38:52 +02:00
Mats 1a4388b3e7 Convert the Elytra sound properly they added in 1.9.2. Fix #430 (#432)
* Convert the Elytra sound properly they added in 1.9.2. Fix #430

* Change wrong version in comment

* Remove the passthrough
2016-06-09 16:03:01 +01:00
Mats f1b81f77d6 Use Myles new Magic transformer for the ArmorListener (#429)
* Use Myles new Magic transformer for the ArmorListener

* Use Myles new Magic transformer for the CommandBlockListener
2016-06-09 10:49:31 +01:00
Myles d6edec3a55 Allow packets to be transformed after sent by specifying the current protocol, woo! 2016-06-09 02:44:12 +01:00
Harry 094f4c6d58 Correct hologram Y offset
https://github.com/filoghost/HolographicDisplays/blob/master/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Offsets.java#L15 The offset is by default -1.25 for 1.8, however 1.9 clients require only -0.29. (-1.25 - -0.29) = -0.96 which should be the correct offset.
2016-06-08 21:43:15 +01:00
Matsv c57d0f60f9 Change to DEV version 2016-06-08 20:40:50 +02:00
Matsv dd5cd1ca48 Remove experimental message from readme 2016-06-08 20:38:39 +02:00
joserobjr ab4a565700 Update README.md (#426) 2016-06-08 20:36:29 +02:00
Myles f8ac54e237 Fix splash potion crash. 2016-06-08 19:02:11 +01:00
joserobjr 089e07fe49 Changes the random chest sounds to normal chest. (#424) 2016-06-08 19:09:19 +02:00
yzimroni 1d3b8f802c Update names from 'snapshot' to '1.10' (#422) 2016-06-08 18:00:21 +02:00
Myles 27283e48a2 Bump the version to avoid bad downloads. 2016-06-08 16:10:59 +01:00
Matsv d22584afbc Bye bungee we will not miss you, you eat our memory 2016-06-08 17:02:13 +02:00
Matsv 249ab01425 Rename package 2016-06-08 16:41:00 +02:00
Matsv 851097de44 Rename package 2016-06-08 16:38:59 +02:00
Matsv c095a51f7c 1.10 support and version bump 2016-06-08 16:35:06 +02:00
Matsv 5e62b7001e Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-06-08 15:35:55 +02:00
Mats 8cc4ba168a Patch EchoPet wrong metadata value (#419) 2016-06-08 11:29:08 +01:00
Matsv 264a21d092 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-06-08 11:37:23 +02:00
Mats e2585db350 Make the main class cleaner by giving the config his own class (#418) 2016-06-07 19:18:48 +02:00
Matsv a2f7a52d31 Support for 1.10-pre2 2016-06-07 17:29:30 +02:00
Matsv d04121202d Modify the config comments to & 1.10 2016-06-07 09:11:33 +02:00
Matsv 95517ade2c Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-06-03 22:18:28 +02:00
Matsv c9441de7e4 Change from info to severe 2016-06-03 21:15:53 +02:00
Matsv dc946cb845 An amazing feature. 2016-06-03 20:49:06 +02:00
Myles 5fa30d9adb Merge branch 'master' into 1.10 2016-06-03 18:52:52 +01:00
Myles a7043424ec Support ProtocolLib + late-bind, also it now tells you the plugin to blame if it goes wrong. How nice! 2016-06-03 18:52:25 +01:00
Matsv e1b3d21732 Woops, the sounds were still broken. Now it should be fixed. no lies this time 2016-06-02 21:19:48 +02:00
Matsv 8ffc056a7e Cleanup and change class/package names from 1.9.3 2016-06-02 21:00:18 +02:00
Matsv 7c274fb03d Handle new sounds 2016-06-02 20:52:55 +02:00
Matsv 3b533b5138 Compatibility with 1.10-pre1 (number 2, git didn't push everything for some reason) 2016-06-02 18:37:05 +02:00
Matsv 5665510f7e Compatibility with 1.10-pre1 2016-06-02 18:34:04 +02:00
Matsv 47faa21ef4 Add latest build to readme & auto-build test 2016-05-27 19:44:21 +02:00
Myles 575f457830 0.8.5 SNAPSHOT 2016-05-27 18:37:34 +01:00
Matsv d5bf7909e8 Sync with master 2016-05-27 19:16:22 +02:00
Myles fdf90b7931 0.8.4 Release 2016-05-27 18:05:46 +01:00
Myles e3305e4eae Automatically buffer unknown entities, removes 2 config values and deprecate api 2016-05-27 01:00:01 +01:00
Matsv 15fe8d394e Support for 16w21b 2016-05-26 19:02:06 +02:00
Matsv 082db38c40 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-05-26 17:03:40 +02:00
Myles be54dc41d1 Add SkinRestorer to plugin.yml unsure if this will fix anything 2016-05-26 14:27:27 +01:00
Myles 487e79c043 Fix javadocs 2016-05-26 13:54:22 +01:00
Matsv ef6390a0b8 Update version for the buildserver 2016-05-26 11:16:12 +02:00
Matsv 9d9fc65633 Support for 16w21a, how fun. A snapshot without packet changes 2016-05-25 16:44:20 +02:00
Matsv 6eac61c912 Sync with master 2016-05-24 21:17:30 +02:00
Matsv d89362a271 Sync with master 2016-05-24 21:13:28 +02:00
Matsv 0a407069fa Add Burger to sources :) 2016-05-24 21:09:04 +02:00
Myles 661fdb1127 Bump version 2016-05-22 11:42:53 +01:00
Myles 9e6c0f72a8 Update version to be 1.9.3/4 2016-05-22 11:41:59 +01:00
Myles 70ad2613f1 Update config to look a bit more nicer, doesn't have the extra space when generated by bukkit. 2016-05-22 11:41:27 +01:00
Myles 9052946c45 Merge branch 'master' into 1.10 2016-05-21 22:12:10 +01:00
Myles 3ddc43bf7b Implement isSpigot() in the API and make it so xray does not activate if they do not use spigot. 2016-05-21 22:07:20 +01:00
Matsv cdb757eae5 Implement Myles idea to just use templates instead of defining compatibility 2016-05-21 21:19:20 +02:00
Matsv 50a3e03649 Possibility to add 'isCompatible' with other types. Fixes crashes on a 1.8 server 2016-05-21 20:54:26 +02:00
Matsv 957284930e Fix sounds 2016-05-21 16:19:12 +02:00
Matsv 198a24371c Sync with master 2016-05-21 15:01:25 +02:00
Mats bcc994b0dd Add possibility to get the supported versions from the api and status response (#402)
* Possibility  to send the Supported versions inside the status response packet

* Make lowercase
2016-05-19 18:56:42 +01:00
Matsv f870bd5a32 Transform pitch correctly 2016-05-19 13:07:51 +02:00
Matsv a14abd6f4f Handle the new Metadata value for 16w20a 2016-05-19 12:18:04 +02:00
Matsv 2456c3cf17 Basic support for the new 1.10 snapshot 16w20a 2016-05-18 16:43:57 +02:00
Myles 9828e4ae1d Add maven default goal.
Requested by our lord and saviour @Aikar
2016-05-17 23:40:00 +01:00
Myles 030aa887c7 Version bump to 0.8.3-SNAPSHOT #hype 2016-05-17 22:56:52 +01:00
Myles ba624a2b48 1.9.4 backport (#399)
Allow 1.9.4 spigot to handle 1.9, 1.9.1, 1.9.2
2016-05-17 22:48:56 +01:00
Myles e7582cb1ec Change license to be more appropriate, this one is more open and allows much more creativity. 2016-05-11 14:19:30 +01:00
Mats 82bf11fcbc Fix anti-xray for 1.9 (#393)
* Fix anti-xray for 1.9

* Bye NMS.

* Perform

* Add config option
2016-05-10 19:53:37 +01:00
Myles ac6e8b1709 Bump version 2016-05-10 15:03:55 +01:00
Myles 00cc545795 Using javassist add compatibility for Spigot builds with protocol compatibility (eg. 1.9.1 & 1.9.2)
Also fix issue with protocol passthrough
*whew*
2016-05-10 14:37:31 +01:00
Myles c7ee816aba Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-05-10 10:27:32 +01:00
Myles e7f85e67f6 Change our data structures to use concurrent equivalents, netty is multi-threaded. 2016-05-10 10:26:49 +01:00
Mats d239d2f967 Stop depending the bossbar metadata rewriter on the bukkit api (#391)
* Don't depend the bossbar metadata rewriter on the bukkit api

* Rename field
2016-05-09 17:44:10 +01:00
Myles 63970c6d82 Version bump 2016-05-08 22:45:46 +01:00
Mats 4cffdd4f64 Don't call PlayerInteractEvent twice and fix the gamemode message (#390)
* Experimental feature to fix the 'Your gamemode has been updated to ' message

* Don't fire PlayerInteractEvent twice, fix #326
2016-05-08 19:49:13 +01:00
Mats cb3f0291b7 Well, this feature never worked. (#389) 2016-05-04 20:03:48 +01:00
Mats edaffad045 1.9.3 support 2016-05-04 17:32:38 +02:00
Myles 172f89940a Implement passthroughAll to make it easier for adding things to the end of a packet. 2016-05-03 21:09:48 +01:00
Myles fb4bc397c6 Add fallback to metaindex to living entity. 2016-04-30 20:40:09 +01:00
Myles e595344941 Implement rewriting for villager trade info. 2016-04-29 17:09:24 +01:00
Myles 05dbc53fd0 Add javassist to the libraries for future use. Apologies for the like extra 600kb :( 2016-04-27 21:57:13 +01:00
Mats 0374da573b Check if the player is ported, fix NullPointerException post 876 on spigotmc.org (#374) 2016-04-24 12:29:52 +01:00
Myles 8e64fb8c21 Add magical unknown protocol version, so that if it's not registered it doesn't NPE. 2016-04-23 20:39:16 +01:00
Myles 0f55402171 Version bump 2016-04-22 17:17:40 +01:00
Myles acb7372eb7 Ensure that the player is online. 2016-04-22 13:36:10 +01:00
Myles 057ab050ea Make it so base protocol actually registers its listeners Credit to @jollyolbrits for finding this silly one. 2016-04-22 13:28:54 +01:00
Myles a01997fe53 Typo 2016-04-21 23:12:40 +01:00
Myles e4adbf4a1a Add terorbility build server and remove filip's since it's dead for now. 2016-04-21 22:47:01 +01:00
Myles 261d68c58e Fix blocking shield not going away :P 2016-04-21 21:13:10 +01:00
Myles 38617d1f61 Version bump 2016-04-21 16:20:45 +01:00
Myles cc5bcb8a64 Allow NMS packet receiving 2016-04-21 16:19:29 +01:00
Mats b6794f5597 Cancel swap item, fix #364 (#365)
* Cancel swap item, fix #364

* Remove canceling drop item
2016-04-20 18:31:18 +01:00
Myles 18f77308f5 Bump version to .0.7.8-SNAPSHOT 2016-04-20 15:59:23 +01:00
Myles 6bb473ea8d Change PlayerMovementMapper to a handler and fix the mappings so it works. 2016-04-20 01:33:01 +01:00
Myles 7984f6400c Clean up not used code 2016-04-20 01:25:00 +01:00
Myles 3498d093b0 Thanks @jollyolbrits for debugging, fix the issue with ground not being sent properly for movement.
Fixes anti-cheat issues.
Placement issues.
Probably 100 billion other issues.
Fix me being an idiot and changing numbers.
2016-04-20 01:22:58 +01:00
Myles a3db32758c Add new is feature to PacketWrapper :) 2016-04-20 01:09:39 +01:00
Myles 45952d3759 Move Idle thread. 2016-04-20 00:50:44 +01:00
Myles c552b12696 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-04-19 22:53:48 +01:00
Myles 0ae9d9dbbf Fix bossbars so they send correctly, credits to @jollyolbrits for detailed reporting of where the issue may take place. 2016-04-19 22:53:27 +01:00
Mats 8852e3b3a3 Change item from main hand, not offhand. Fixes #329 (#363) 2016-04-19 16:32:03 +01:00
Myles a496b2d3a8 This branch would be better here as it won't be applicable if elsewhere. 2016-04-17 12:59:01 +01:00
Myles 6198dbfe70 Change to match the config 2016-04-17 12:55:34 +01:00
Myles a81d52a54f Implement a packet limiting system,
This means that if a client sends more than a maximum, it will be disconnected.
It also means if a client goes over a threshold multiple times over a period of time, it will be disconnected.

Improvements are welcome, it's mostly down to how the user configures it.
(Some values are higher so that we don't kill every server with a tiny bit of lag)
2016-04-17 12:55:18 +01:00
Myles cf20e8ac5f Bump version 2016-04-17 00:53:49 +01:00
Myles 4f8a248c6b Add item-cache as a less expensive way of getting items. (Will rewrite eventually to not depend on Bukkit) 2016-04-17 00:52:35 +01:00
Mats 57d58db35e Fix item showing twice, one in the new slot (#359) 2016-04-15 18:48:28 +01:00
Mats c214ca02e8 Make commandblocks compatible with all 1.8 spigot/craftbukkit versions, fix #355 (#356) 2016-04-14 12:59:23 +01:00
Myles 6b8b0e1771 Bump version to 0.7.6-SNAPSHOT 2016-04-13 19:42:53 +01:00
Myles e6671513f2 Improve an issue to do with block breaking ghosting back, note this is not a fully fix, but improves the issue quite a bit. 2016-04-13 16:54:18 +01:00
Myles 4c7b50d6fb Fix chunk sending order, Fixes #338, #292 (I believe.) 2016-04-13 15:05:50 +01:00
Mats 1c9229a2fc Don't unload the chunks twice (#350) 2016-04-13 12:25:41 +01:00
Mats d11c18c024 Remove MCProtocolLib (#349) 2016-04-12 12:40:12 +01:00
Mats dc4b5b4d35 Possibility to show the new 1.9 death messages (#348) 2016-04-12 12:01:37 +01:00
Mats 517299d673 Show skeletons' bow like 1.8, not down as 1.9 without target (#347) 2016-04-12 11:18:58 +01:00
Mats a8baa7af50 Transform Combat packet (#346) 2016-04-12 11:18:51 +01:00
Myles a1f847c905 Patch respawn packet to reset bulk / loaded cache. 2016-04-11 20:17:48 +01:00
Mats f3ea43b1eb Reformat / optimize imports (#344) 2016-04-10 18:43:08 +01:00
Mats 551b5d0654 Rewrite effect ids (#342)
* Rewrite effect ids

* Remove test line

* Remove Lombok unused thingy

* No need to check.
2016-04-10 14:29:25 +01:00
Mats 9f39662df6 Check if custom name is visible and if custom name is not empty. Fix …
* Check if custom name is visible and if custom name is not empty. Fix #327

* Fix typo
2016-04-06 13:50:09 +01:00
96 changed files with 3369 additions and 623 deletions

No files matched your search

+22 -1
View File
@@ -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.
+8 -15
View File
@@ -1,14 +1,9 @@
# ViaVersion 0.7.4
# ViaVersion 0.9.3
[![Build Status](https://travis-ci.org/MylesIsCool/ViaVersion.svg?branch=master)](https://travis-ci.org/MylesIsCool/ViaVersion)
[![Gitter](https://badges.gitter.im/MylesIsCool/ViaVersion.svg)](https://gitter.im/MylesIsCool/ViaVersion)
**Allows the connection of 1.9 clients to 1.8**
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
###*Don't use late bind*
This uses our new API, we currently don't have javadocs yet but look at one of our protocols on how to get started then use ProtocolRegistry.registerProtocol()
**1.10 clients on 1.8 / 1.9 servers**
This took 100s hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
@@ -20,7 +15,7 @@ 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)
@@ -56,20 +51,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
**JavaDocs:** https://jd.matsv.nl/
**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!
+29 -1
View File
@@ -6,7 +6,7 @@
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>0.7.5-SNAPSHOT</version>
<version>0.9.4</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 + "===============================================");
}
}
@@ -0,0 +1,157 @@
package us.myles.ViaVersion;
import us.myles.ViaVersion.api.ViaVersionConfig;
import us.myles.ViaVersion.util.Configuration;
import java.io.File;
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);
}
public boolean isAutoTeam() {
// Collision has to be enabled first
return isPreventCollision() && plugin.getConfig().getBoolean("auto-team", true);
}
}
@@ -1,48 +0,0 @@
package us.myles.ViaVersion;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import org.bukkit.scheduler.BukkitRunnable;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.MovementTracker;
import us.myles.ViaVersion.util.PipelineUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.util.Map;
import java.util.UUID;
public class ViaIdleThread extends BukkitRunnable {
private final Map<UUID, UserConnection> portedPlayers;
private final Object idlePacket;
public ViaIdleThread(Map<UUID, UserConnection> portedPlayers) {
this.portedPlayers = portedPlayers;
try {
Class<?> idlePacketClass = ReflectionUtil.nms("PacketPlayInFlying");
idlePacket = idlePacketClass.newInstance();
} catch (InstantiationException | IllegalArgumentException | IllegalAccessException | ClassNotFoundException e) {
throw new RuntimeException("Couldn't find/make player idle packet, help!", e);
}
}
@Override
public void run() {
for (UserConnection info : portedPlayers.values()) {
if (info.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) {
long nextIdleUpdate = info.get(MovementTracker.class).getNextIdlePacket();
if (nextIdleUpdate <= System.currentTimeMillis()) {
ChannelHandlerContext context = PipelineUtil.getContextBefore("decoder", info.getChannel().pipeline());
if(info.getChannel().isOpen()) {
if (context != null) {
context.fireChannelRead(idlePacket);
info.get(MovementTracker.class).incrementIdlePacket();
}
}
}
}
}
}
}
@@ -5,14 +5,15 @@ 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 org.bukkit.Bukkit;
import org.bukkit.entity.Player;
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.ViaVersionConfig;
import us.myles.ViaVersion.api.boss.BossBar;
import us.myles.ViaVersion.api.boss.BossColor;
import us.myles.ViaVersion.api.boss.BossStyle;
@@ -20,39 +21,45 @@ 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.classgenerator.ClassGenerator;
import us.myles.ViaVersion.commands.ViaCommandHandler;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
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.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.ArrayList;
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;
import java.util.concurrent.TimeUnit;
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVersionConfig {
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
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;
@Override
public void onLoad() {
// Config magic
conf = new ViaConfig(this);
ViaVersion.setInstance(this);
generateConfig();
// Handle reloads
if (System.getProperty("ViaVersion") != null) {
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.");
@@ -66,13 +73,37 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
}
}
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now loaded, injecting.");
injectPacketHandler();
// Spigot detector
try {
Class.forName("org.spigotmc.SpigotConfig");
} catch (ClassNotFoundException e) {
spigot = false;
}
// Check if it's a spigot build with a protocol mod
try {
compatSpigotBuild = ReflectionUtil.nms("PacketEncoder").getDeclaredField("version") != null;
} catch (Exception e) {
compatSpigotBuild = false;
}
// Check if we're using protocol support too
protocolSupport = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
// Generate classes needed (only works if it's compat or ps)
ClassGenerator.generate();
lateBind = !isBinded();
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
if (!lateBind)
injectPacketHandler();
}
@Override
public void onEnable() {
if (isCheckForUpdates())
if (lateBind)
injectPacketHandler();
if (conf.isCheckForUpdates())
UpdateUtil.sendUpdateMessage(this);
// Gather version :)
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
@@ -86,6 +117,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
getLogger().warning("ViaVersion will not function on the current protocol.");
}
}
ProtocolRegistry.refreshVersions();
}
});
@@ -97,6 +129,11 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
// 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
@@ -152,42 +189,25 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
}
}
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));
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);
}
}
}
newConfig.save();
} else {
saveDefaultConfig();
}
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;
@@ -228,6 +248,33 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
}
}
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();
@@ -238,11 +285,18 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
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();
}
}
@@ -284,17 +338,25 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
@Override
public int getPlayerVersion(@NonNull Player player) {
if (!isPorted(player))
return ProtocolRegistry.SERVER_PROTOCOL;
return getExternalVersion(player);
return portedPlayers.get(player.getUniqueId()).get(ProtocolInfo.class).getProtocolVersion();
}
@Override
public int getPlayerVersion(@NonNull UUID uuid) {
if (!isPorted(uuid))
return ProtocolRegistry.SERVER_PROTOCOL;
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
public boolean isPorted(UUID playerUUID) {
return portedPlayers.containsKey(playerUUID);
@@ -348,49 +410,19 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
return commandHandler;
}
public boolean isCheckForUpdates() {
return getConfig().getBoolean("checkforupdates", true);
@Override
public boolean isCompatSpigotBuild() {
return compatSpigotBuild;
}
public boolean isPreventCollision() {
return getConfig().getBoolean("prevent-collision", true);
@Override
public SortedSet<Integer> getSupportedVersions() {
return ProtocolRegistry.getSupportedVersions();
}
public boolean isNewEffectIndicator() {
return getConfig().getBoolean("use-new-effect-indicator", true);
}
public boolean isSuppressMetadataErrors() {
return getConfig().getBoolean("suppress-metadata-errors", false);
}
public boolean isShieldBlocking() {
return getConfig().getBoolean("shield-blocking", true);
}
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 boolean isUnknownEntitiesSuppressed() {
return getConfig().getBoolean("suppress-entityid-errors", false);
}
public double getHologramYOffset() {
return getConfig().getDouble("hologram-y", -1D);
}
public boolean isAutoTeam() {
// Collision has to be enabled first
return isPreventCollision() && getConfig().getBoolean("auto-team", true);
@Override
public boolean isSpigot() {
return this.spigot;
}
public void addPortedClient(UserConnection info) {
@@ -420,7 +452,41 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
}
}
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;
}
}
@@ -6,15 +6,20 @@ import io.netty.buffer.Unpooled;
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;
@@ -38,8 +43,10 @@ public class PacketWrapper {
* 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;
@@ -56,12 +63,55 @@ public class PacketWrapper {
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;
@@ -82,6 +132,7 @@ public class PacketWrapper {
* 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
*/
@@ -97,7 +148,7 @@ public class PacketWrapper {
}
} else {
Pair<Type, Object> read = readableObjects.poll();
if (read.getKey().equals(type)) {
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) {
@@ -114,6 +165,7 @@ public class PacketWrapper {
* 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) {
@@ -134,6 +186,7 @@ public class PacketWrapper {
* 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
*/
@@ -143,6 +196,21 @@ public class PacketWrapper {
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.
*
@@ -189,6 +257,14 @@ public class PacketWrapper {
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);
@@ -200,10 +276,44 @@ public class PacketWrapper {
* 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 {
if (!isCancelled()) {
// 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 = i + 1;
break;
}
}
// 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);
user().sendRawPacket(output);
}
}
/**
* 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);
@@ -234,6 +344,26 @@ public class PacketWrapper {
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
*/
@@ -283,4 +413,13 @@ public class PacketWrapper {
PipelineUtil.getContextBefore("decompress", user().getChannel().pipeline()).fireChannelRead(output);
}
}
@Override
public String toString() {
return "PacketWrapper{" +
"packetValues=" + packetValues +
", readableObjects=" + readableObjects +
", id=" + id +
'}';
}
}
@@ -14,6 +14,6 @@ public class ViaVersion {
public static void setInstance(ViaVersionPlugin plugin) {
Validate.isTrue(instance == null, "Instance is already set");
ViaVersion.instance = plugin;
ViaVersion.config = plugin;
ViaVersion.config = plugin.getConf();
}
}
@@ -7,6 +7,7 @@ import us.myles.ViaVersion.api.boss.BossColor;
import us.myles.ViaVersion.api.boss.BossStyle;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import java.util.SortedSet;
import java.util.UUID;
public interface ViaVersionAPI {
@@ -20,6 +21,7 @@ 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)
@@ -37,7 +39,7 @@ public interface ViaVersionAPI {
/**
* 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);
@@ -101,4 +103,35 @@ public interface ViaVersionAPI {
* @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
*
* @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();
}
@@ -2,13 +2,6 @@ package us.myles.ViaVersion.api;
public interface ViaVersionConfig {
/**
* Get if global debug is enabled
*
* @return true if debug is enabled
*/
boolean isDebug();
/**
* Get if the plugin should check for updates
*
@@ -24,12 +17,19 @@ public interface ViaVersionConfig {
boolean isPreventCollision();
/**
* Get if 1.9 clients are shown the new effect indicator in the top-right corner
* 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
*
@@ -38,28 +38,28 @@ public interface ViaVersionConfig {
boolean isSuppressMetadataErrors();
/**
* Get if blocking in 1.9 appears as a player holding a shield
* 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
* 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 clients
* 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 clients are being stopped from flickering
* 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
@@ -71,6 +71,7 @@ public interface ViaVersionConfig {
*
* @return true if boss bar patching is enabled
*/
@Deprecated
boolean isUnknownEntitiesSuppressed();
/**
@@ -86,4 +87,89 @@ public interface ViaVersionConfig {
* @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 '&amp;'
*/
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 '&amp;'
*/
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();
}
@@ -17,6 +17,7 @@ public interface BossBar {
* Change the title
*
* @param title Title can be in either JSON or just text
* @return The BossBar object
*/
BossBar setTitle(String title);
@@ -31,13 +32,14 @@ public interface BossBar {
* Change the health
*
* @param health this float has to be between 0F - 1F
* @return The BossBar object
*/
BossBar setHealth(float health);
/**
* Get the bossbar color
*
* @return
* @return The colour
*/
BossColor getColor();
@@ -45,6 +47,7 @@ public interface BossBar {
* Yay colors!
*
* @param color Whatever color you want!
* @return The BossBar object
*/
BossBar setColor(BossColor color);
@@ -59,48 +62,61 @@ public interface BossBar {
* 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
*/
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 instance
* @return The BossBar object
*/
BossBar addPlayers(Player...players);
BossBar addPlayers(Player... players);
/**
* Remove the bossbar from a player
*
* @param player
* @param player The player
* @return The BossBar object
*/
BossBar removePlayer(Player player);
/**
* Add flags
*
* @param flag
* @param flag The flag to add
* @return The BossBar object
*/
BossBar addFlag(BossFlag flag);
/**
* Remove flags.
*
* @param flag
* @param flag The flag to remove
* @return The BossBar object
*/
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);
@@ -113,11 +129,15 @@ public interface BossBar {
/**
* Show the bossbar to everyone (In the getPlayer set)
*
* @return The BossBar object
*/
BossBar show();
/**
* Hide the bossbar from everyone (In the getPlayer set)
*
* @return The BossBar object
*/
BossBar hide();
@@ -10,14 +10,14 @@ public abstract class ViaSubCommand {
/**
* Subcommand name
*
* @return your input
* @return The command name
*/
public abstract String name();
/**
* subcommand description, this'll show in /viaversion list
*
* @return your input
* @return The command description
*/
public abstract String description();
@@ -25,7 +25,7 @@ public abstract class ViaSubCommand {
* Usage example:
* "playerversion [name]"
*
* @return your input
* @return The command usage
*/
public String usage() {
return name();
@@ -34,7 +34,7 @@ public abstract class ViaSubCommand {
/**
* Permission, null for everyone
*
* @return
* @return The permission required to use the command
*/
public String permission() {
return "viaversion.admin";
@@ -67,11 +67,11 @@ public abstract class ViaSubCommand {
/**
* Send message formatted / colored
*
* @param sender command sender
* @param sender command sender
* @param message string message
* @param args optional objects
* @param args optional objects
*/
public void sendMessage(CommandSender sender, String message, Object... args){
public void sendMessage(CommandSender sender, String message, Object... args) {
ViaCommandHandler.sendMessage(sender, message, args);
}
}
@@ -3,26 +3,32 @@ package us.myles.ViaVersion.api.data;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.socket.SocketChannel;
import lombok.Getter;
import lombok.Setter;
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 {
@Getter
private final SocketChannel channel;
Map<Class, StoredObject> storedObjects = new ConcurrentHashMap<>();
@Getter
@Setter
private boolean active = true;
@Getter
@Setter
private boolean pendingDisconnect = false;
private Object lastPacket;
@Getter
private long sentPackets = 0L;
@Getter
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) {
@@ -33,6 +39,7 @@ public class UserConnection {
* 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) {
@@ -96,8 +103,48 @@ public class UserConnection {
/**
* Used for incrementing the number of packets received from the client
*
* @return True if the interval has reset
*/
public void incrementReceived() {
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(); // =)
}
}
@@ -1,12 +1,14 @@
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;
@@ -1,38 +1,4 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
@RequiredArgsConstructor
@Getter
@ToString
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
*/
public Chunk(int x, int z) {
this(x, z, true, 0, new ChunkSection[16], null);
this.unloadPacket = true;
}
/**
* Does this chunk have biome data
*
* @return True if the chunk has biome data
*/
public boolean hasBiomeData() {
return biomeData != null && groundUp;
}
public interface Chunk {
}
@@ -2,11 +2,11 @@ package us.myles.ViaVersion.api.protocol;
import lombok.AllArgsConstructor;
import lombok.Getter;
import us.myles.ViaVersion.exception.CancelException;
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;
@@ -7,17 +7,16 @@ 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.BaseProtocol;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
public class ProtocolPipeline extends Protocol {
LinkedList<Protocol> protocolList;
List<Protocol> protocolList;
private UserConnection userConnection;
public ProtocolPipeline(UserConnection userConnection) {
@@ -27,9 +26,9 @@ public class ProtocolPipeline extends Protocol {
@Override
protected void registerPackets() {
protocolList = new LinkedList<>();
protocolList = new CopyOnWriteArrayList<>();
// This is a pipeline so we register basic pipes
protocolList.addLast(new BaseProtocol());
protocolList.add(ProtocolRegistry.BASE_PROTOCOL);
}
@Override
@@ -55,7 +54,7 @@ public class ProtocolPipeline extends Protocol {
*/
public void add(Protocol protocol) {
if (protocolList != null) {
protocolList.addLast(protocol);
protocolList.add(protocol);
protocol.init(userConnection);
} else {
throw new NullPointerException("Tried to add protocol to early");
@@ -70,11 +69,9 @@ public class ProtocolPipeline extends Protocol {
if (direction == Direction.OUTGOING)
Collections.reverse(protocols);
for (Protocol protocol : protocols) { // Copy to prevent from removal.
protocol.transform(direction, state, packetWrapper);
// Reset the reader for the packetWrapper (So it can be recycled across packets)
packetWrapper.resetReader();
}
// Apply protocols
packetWrapper.apply(direction, state, 0, protocols);
super.transform(direction, state, packetWrapper);
if (ViaVersion.getInstance().isDebug()) {
@@ -83,46 +80,49 @@ public class ProtocolPipeline extends Protocol {
// 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_2.getId()) {
ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_9_3.getId()) {
PacketType type;
if (ProtocolRegistry.SERVER_PROTOCOL == ProtocolVersion.v1_8.getId()) {
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 {
if (direction == Direction.INCOMING) {
type = PacketType.findOldPacket(state, direction, originalID);
} else {
type = PacketType.findNewPacket(state, direction, originalID);
}
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;
// 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();
packet = type.name();
}
}
String name = packet + "[" + userConnection.get(ProtocolInfo.class).getProtocolVersion() + "]";
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
plugin.getLogger().log(Level.INFO, "{0}: {1} {2} -> {3} [{4}]",
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
name,
packetWrapper
});
}
}
@@ -158,4 +158,8 @@ public class ProtocolPipeline extends Protocol {
return false;
}
public List<Protocol> pipes() {
return protocolList;
}
}
@@ -1,23 +1,41 @@
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 HashMap<>();
private static Map<Pair<Integer, Integer>, List<Pair<Integer, Protocol>>> pathCache = new HashMap<>();
private static List<Protocol> registerList = new ArrayList<>();
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());
}
/**
@@ -42,11 +60,29 @@ public class ProtocolRegistry {
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());
}
}
public static SortedSet<Integer> getSupportedVersions() {
return Collections.unmodifiableSortedSet(new TreeSet<>(supportedVersions));
}
/**
* Check if this plugin is useful to the server.
*
@@ -78,6 +114,7 @@ public class ProtocolRegistry {
* @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
@@ -91,6 +128,8 @@ public class ProtocolRegistry {
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)) {
@@ -103,13 +142,16 @@ public class ProtocolRegistry {
// Calculate the rest of the protocol using the current
newCurrent = getProtocolPath(newCurrent, entry.getKey(), serverVersion);
if (newCurrent != null) {
return newCurrent;
// If it's shorter then choose it
if (shortest == null || shortest.size() > newCurrent.size()) {
shortest = newCurrent;
}
}
}
}
}
return null;
return shortest; // null if none found
}
/**
@@ -1,35 +1,61 @@
package us.myles.ViaVersion.api.protocol;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import java.util.*;
@Data
@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) {
@@ -37,10 +63,32 @@ public class ProtocolVersion {
}
public static ProtocolVersion getProtocol(int id) {
return versions.get(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<ProtocolVersion>(versions.values()));
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;
}
}
@@ -39,7 +39,9 @@ public abstract class PacketRemapper {
* 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);
@@ -49,7 +51,8 @@ public abstract class PacketRemapper {
* 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 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));
@@ -10,6 +10,7 @@ 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.List;
import java.util.UUID;
@Getter
@@ -59,8 +60,10 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
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();
@@ -78,6 +81,10 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
this.typeName = typeName;
}
public Class<? extends Type> getBaseClass() {
return this.getClass();
}
@Override
public String toString() {
return "Type|" + getTypeName();
@@ -13,38 +13,9 @@ public class ArrayType<T> extends Type<T[]> {
this.elementType = type;
}
/* Taken from http://stackoverflow.com/questions/4901128/obtaining-the-array-class-of-a-component-type */
public static Class<?> getArrayClass(Class<?> componentType) {
ClassLoader classLoader = componentType.getClassLoader();
String name;
if (componentType.isArray()) {
// just add a leading "["
name = "[" + componentType.getName();
} else if (componentType == boolean.class) {
name = "[Z";
} else if (componentType == byte.class) {
name = "[B";
} else if (componentType == char.class) {
name = "[C";
} else if (componentType == double.class) {
name = "[D";
} else if (componentType == float.class) {
name = "[F";
} else if (componentType == int.class) {
name = "[I";
} else if (componentType == long.class) {
name = "[J";
} else if (componentType == short.class) {
name = "[S";
} else {
// must be an object non-array class
name = "[L" + componentType.getName() + ";";
}
try {
return classLoader != null ? classLoader.loadClass(name) : Class.forName(name);
} catch (ClassNotFoundException e) {
return null; // oh
}
// Should only happen once per class init.
return Array.newInstance(componentType, 0).getClass();
}
@Override
@@ -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,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,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);
}
}
@@ -78,10 +78,15 @@ public class ViaBossBar implements BossBar {
@Override
public BossBar addPlayer(@NonNull Player player) {
if (!players.contains(player.getUniqueId())) {
players.add(player.getUniqueId());
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;
}
@@ -153,14 +158,16 @@ 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) || !(ViaVersion.getInstance().getPlayerVersion(uuid) >= ProtocolVersion.v1_9.getId())) {
players.remove(uuid);
buf.release();
return;
}
ViaVersion.getInstance().sendRawPacket(uuid, buf);
@@ -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);
}
@@ -130,6 +130,7 @@ public class ViaCommandHandler implements ViaVersionCommand, CommandExecutor, Ta
private void registerDefaults() throws Exception {
registerSubCommand(new ListSubCmd());
registerSubCommand(new PPSSubCmd());
registerSubCommand(new DebugSubCmd());
registerSubCommand(new DisplayLeaksSubCmd());
registerSubCommand(new DontBugMeSubCmd());
@@ -20,7 +20,7 @@ public class AutoTeamSubCmd extends ViaSubCommand {
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
boolean newValue = !plugin.isAutoTeam();
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"));
@@ -21,7 +21,7 @@ public class DebugSubCmd extends ViaSubCommand {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
plugin.setDebug(!plugin.isDebug());
sendMessage(sender, "&6Debug mode is now %s" , (plugin.isDebug() ? "&aenabled" : "&cdisabled"));
sendMessage(sender, "&6Debug mode is now %s", (plugin.isDebug() ? "&aenabled" : "&cdisabled"));
return true;
}
}
@@ -20,7 +20,7 @@ public class DontBugMeSubCmd extends ViaSubCommand {
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
boolean newValue = !plugin.isCheckForUpdates();
boolean newValue = !ViaVersion.getConfig().isCheckForUpdates();
plugin.getConfig().set("checkforupdates", newValue);
plugin.saveConfig();
sendMessage(sender, "&6We will %snotify you about updates.", (newValue ? "&a" : "&cnot "));
@@ -27,19 +27,25 @@ public class ListSubCmd extends ViaSubCommand {
@Override
public boolean execute(CommandSender sender, String[] args) {
Map<Integer, Set<String>> playerVersions = new HashMap<>();
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);
if (!playerVersions.containsKey(playerVersion))
playerVersions.put(playerVersion, new HashSet<String>());
playerVersions.get(playerVersion).add(p.getName());
ProtocolVersion key = ProtocolVersion.getProtocol(playerVersion);
if (!playerVersions.containsKey(key))
playerVersions.put(key, new HashSet<String>());
playerVersions.get(key).add(p.getName());
}
Map<Integer, Set<String>> sorted = new TreeMap<>(playerVersions);
for (Map.Entry<Integer, Set<String>> entry : sorted.entrySet())
sendMessage(sender, "&8[&6%s&8]: &b%s", ProtocolVersion.getProtocol(entry.getKey()).getName(), entry.getValue());
for (Map.Entry<ProtocolVersion, Set<String>> entry : playerVersions.entrySet())
sendMessage(sender, "&8[&6%s&8]: &b%s", entry.getKey().getName(), entry.getValue());
sorted.clear();
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;
}
}
@@ -20,7 +20,7 @@ public class ReloadSubCmd extends ViaSubCommand {
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
plugin.generateConfig();
plugin.getConf().generateConfig();
sendMessage(sender, "&6Configuration successfully reloaded! Some features may need a restart.");
return true;
}
@@ -21,9 +21,9 @@ public class InformativeException extends Exception {
}
private String getSource(Class<?> sourceClazz) {
if(sourceClazz.isAnonymousClass()){
if (sourceClazz.isAnonymousClass()) {
return sourceClazz.getName() + " (Anonymous)";
}else{
} else {
return sourceClazz.getName();
}
}
@@ -3,7 +3,9 @@ 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.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;
@@ -29,9 +31,19 @@ public class ViaDecodeHandler extends ByteToMessageDecoder {
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()) {
// Increment received
info.incrementReceived();
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
@@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
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;
@@ -11,6 +12,7 @@ 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 {
@@ -25,6 +27,11 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
@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
@@ -39,9 +46,9 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
if (bytebuf.readableBytes() == 0) {
throw new CancelException();
}
// Increment sent
info.incrementSent();
if (info.isActive()) {
// Increment sent
info.incrementSent();
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
@@ -49,10 +56,10 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
bytebuf.clear();
try {
PacketWrapper wrapper = new PacketWrapper(id, oldPacket, info);
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
protInfo.getPipeline().transform(Direction.OUTGOING, protInfo.getState(), wrapper);
wrapper.writeToBuffer(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;
@@ -17,9 +17,9 @@ public class ViaPacketHandler extends MessageToMessageEncoder {
@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.
// 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 :) */
@@ -7,6 +7,8 @@ import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
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;
@@ -36,9 +38,11 @@ public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
new ProtocolPipeline(info);
// Add originals
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"));
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);
@@ -1,5 +1,6 @@
package us.myles.ViaVersion.protocols.base;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -18,10 +19,12 @@ import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.State;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
public class BaseProtocol extends Protocol {
@@ -40,6 +43,8 @@ public class BaseProtocol extends Protocol {
try {
JSONObject json = (JSONObject) new JSONParser().parse(originalStatus);
JSONObject version = (JSONObject) json.get("version");
if (ViaVersion.getConfig().isSendSupportedVersions())
version.put("supportedVersions", ViaVersion.getInstance().getSupportedVersions());
if (ProtocolRegistry.SERVER_PROTOCOL == -1) {
Long original = (Long) version.get("protocol");
ProtocolRegistry.SERVER_PROTOCOL = original.intValue();
@@ -83,6 +88,15 @@ public class BaseProtocol extends Protocol {
info.setUsername(wrapper.get(Type.STRING, 1));
// Add to ported clients
((ViaVersionPlugin) ViaVersion.getInstance()).addPortedClient(wrapper.user());
if (ViaVersion.getInstance().isDebug()) {
// Print out the route to console
((ViaVersionPlugin) ViaVersion.getInstance()).getLogger().log(Level.INFO, "{0} logged in with protocol {1}, Route: {2}",
new Object[]{
wrapper.get(Type.STRING, 1),
info.getProtocolVersion(),
StringUtils.join(info.getPipeline().pipes(), ", ")
});
}
}
});
}
@@ -157,4 +171,15 @@ public class BaseProtocol extends Protocol {
}
}, plugin);
}
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
super.transform(direction, state, packetWrapper);
if (direction == Direction.INCOMING && state == State.HANDSHAKE) {
// Disable if it isn't a handshake packet.
if (packetWrapper.getId() > 0) {
packetWrapper.user().setActive(false);
}
}
}
}
@@ -0,0 +1,163 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.remapper.ValueTransformer;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.storage.ResourcePackTracker;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types.Meta1_10Type;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types.MetaList1_10Type;
import java.util.List;
public class Protocol1_10To1_9_3_4 extends Protocol {
public static final Type<List<Metadata>> METADATA_LIST = new MetaList1_10Type();
public static final Type<Metadata> METADATA = new Meta1_10Type();
public static ValueTransformer<Short, Float> toNewPitch = new ValueTransformer<Short, Float>(Type.FLOAT) {
@Override
public Float transform(PacketWrapper wrapper, Short inputValue) throws Exception {
return inputValue / 63.5F;
}
};
@Override
protected void registerPackets() {
// Named sound effect
registerOutgoing(State.PLAY, 0x19, 0x19, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // 0 - Sound name
map(Type.VAR_INT); // 1 - Sound Category
map(Type.INT); // 2 - x
map(Type.INT); // 3 - y
map(Type.INT); // 4 - z
map(Type.FLOAT); // 5 - Volume
map(Type.UNSIGNED_BYTE, toNewPitch); // 6 - Pitch
}
});
// Sound effect
registerOutgoing(State.PLAY, 0x46, 0x46, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Sound name
map(Type.VAR_INT); // 1 - Sound Category
map(Type.INT); // 2 - x
map(Type.INT); // 3 - y
map(Type.INT); // 4 - z
map(Type.FLOAT); // 5 - Volume
map(Type.UNSIGNED_BYTE, toNewPitch); // 6 - Pitch
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int id = wrapper.get(Type.VAR_INT, 0);
wrapper.set(Type.VAR_INT, 0, getNewSoundId(id));
}
});
}
});
// Metadata packet
registerOutgoing(State.PLAY, 0x39, 0x39, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity ID
map(METADATA_LIST); // 1 - Metadata list
}
});
// Spawn Mob
registerOutgoing(State.PLAY, 0x03, 0x03, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity id
map(Type.UUID); // 1 - UUID
map(Type.UNSIGNED_BYTE); // 2 - Entity Type
map(Type.DOUBLE); // 3 - X
map(Type.DOUBLE); // 4 - Y
map(Type.DOUBLE); // 5 - Z
map(Type.BYTE); // 6 - Yaw
map(Type.BYTE); // 7 - Pitch
map(Type.BYTE); // 8 - Head Pitch
map(Type.SHORT); // 9 - Velocity X
map(Type.SHORT); // 10 - Velocity Y
map(Type.SHORT); // 11 - Velocity Z
map(METADATA_LIST); // 12 - Metadata
}
});
// Spawn Player
registerOutgoing(State.PLAY, 0x05, 0x05, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity ID
map(Type.UUID); // 1 - Player UUID
map(Type.DOUBLE); // 2 - X
map(Type.DOUBLE); // 3 - Y
map(Type.DOUBLE); // 4 - Z
map(Type.BYTE); // 5 - Yaw
map(Type.BYTE); // 6 - Pitch
map(METADATA_LIST); // 7 - Metadata list
}
});
// Packet Send ResourcePack
registerOutgoing(State.PLAY, 0x32, 0x32, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // 0 - URL
map(Type.STRING); // 1 - Hash
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ResourcePackTracker tracker = wrapper.user().get(ResourcePackTracker.class);
tracker.setLastHash(wrapper.get(Type.STRING, 1)); // Store the hash for resourcepack status
}
});
}
});
// Packet ResourcePack status
registerIncoming(State.PLAY, 0x16, 0x16, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ResourcePackTracker tracker = wrapper.user().get(ResourcePackTracker.class);
wrapper.write(Type.STRING, tracker.getLastHash());
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT));
}
});
}
});
}
public int getNewSoundId(int id) { //TODO Make it better, suggestions are welcome. It's ugly and hardcoded now.
int newId = id;
if (id >= 24) //Blame the enchantment table sound
newId += 1;
if (id >= 248) //Blame the husk
newId += 4;
if (id >= 296) //Blame the polar bear
newId += 6;
if (id >= 354) //Blame the stray
newId += 4;
if (id >= 372) //Blame the wither skeleton
newId += 4;
return newId;
}
@Override
public void init(UserConnection userConnection) {
userConnection.put(new ResourcePackTracker(userConnection));
}
}
@@ -0,0 +1,18 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3.storage;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
@Getter
@Setter
@ToString
public class ResourcePackTracker extends StoredObject {
private String lastHash = "";
public ResourcePackTracker(UserConnection user) {
super(user);
}
}
@@ -0,0 +1,30 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types;
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 Meta1_10Type 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,41 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.minecraft.MetaListTypeTemplate;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.NewType;
import java.util.ArrayList;
import java.util.List;
public class MetaList1_10Type extends MetaListTypeTemplate {
@Override
public List<Metadata> read(ByteBuf buffer) throws Exception {
List<Metadata> list = new ArrayList<>();
Metadata meta;
do {
meta = Protocol1_10To1_9_3_4.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) {
if (m.getId() >= 5)
m.setId(m.getId() + 1);
if (m.getId() == 4)
Protocol1_10To1_9_3_4.METADATA.write(buffer, new Metadata(5, NewType.Boolean.getTypeID(), Type.BOOLEAN, false)); // No gravity metadata
Protocol1_10To1_9_3_4.METADATA.write(buffer, m);
}
// Write end of list
Protocol1_10To1_9_3_4.METADATA.write(buffer, null);
}
}
@@ -0,0 +1,86 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4;
import lombok.Getter;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BlockEntity {
@Getter
private static final Map<String, Integer> types = new ConcurrentHashMap<>();
static {
types.put("MobSpawner", 1);
types.put("Control", 2);
types.put("Beacon", 3);
types.put("Skull", 4);
types.put("FlowerPot", 5);
types.put("Banner", 6);
types.put("UNKNOWN", 7);
types.put("EndGateway", 8);
types.put("Sign", 9);
//I didn't see anything that didn't work about chests
// types.put("Chest", -1);
}
public static void handle(List<CompoundTag> tags, UserConnection connection) {
for (CompoundTag tag : tags) {
try {
if (!tag.contains("id"))
throw new Exception("NBT tag not handled because the id key is missing");
String id = (String) tag.get("id").getValue();
if (!types.containsKey(id))
throw new Exception("Not handled id: " + id);
int newId = types.get(id);
if (newId == -1)
continue;
int x = (int) tag.get("x").getValue();
int y = (int) tag.get("y").getValue();
int z = (int) tag.get("z").getValue();
Position pos = new Position((long) x, (long) y, (long) z);
if (newId != 9) {
updateBlockEntity(pos, (short) newId, tag, connection);
} else {
String[] lines = new String[4];
for (int i = 1; i < 5; i++)
lines[i - 1] = (String) tag.get("Text" + i).getValue();
updateSign(pos, lines, connection);
}
} catch (Exception e) {
if (ViaVersion.getInstance().isDebug()) {
System.out.println("Block Entity: " + e.getMessage() + ": " + tag);
}
}
}
}
private static void updateBlockEntity(Position pos, short id, CompoundTag tag, UserConnection connection) throws Exception {
PacketWrapper wrapper = new PacketWrapper(0x09, null, connection);
wrapper.write(Type.POSITION, pos);
wrapper.write(Type.UNSIGNED_BYTE, id);
wrapper.write(Type.NBT, tag);
wrapper.send(Protocol1_9_1_2TO1_9_3_4.class);
}
private static void updateSign(Position pos, String[] lines, UserConnection connection) throws Exception {
PacketWrapper wrapper = new PacketWrapper(0x46, null, connection);
wrapper.write(Type.POSITION, pos);
for (String s : lines)
wrapper.write(Type.STRING, s);
wrapper.send(Protocol1_9_1_2TO1_9_3_4.class);
}
}
@@ -0,0 +1,20 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import java.util.List;
@Data
@AllArgsConstructor
public class Chunk1_9_3_4 implements Chunk {
private int x;
private int z;
private boolean groundUp;
private int bitmask;
private byte[] sections;
List<CompoundTag> blockEntities;
}
@@ -0,0 +1,54 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4;
import io.netty.buffer.ByteBuf;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
import java.util.ArrayList;
import java.util.List;
public class Chunk1_9_3_4Type extends BaseChunkType {
public Chunk1_9_3_4Type() {
super("1.9.3 Chunk");
}
@Override
public Chunk read(ByteBuf input) throws Exception {
int chunkX = input.readInt();
int chunkZ = input.readInt();
boolean groundUp = input.readBoolean();
int primaryBitmask = Type.VAR_INT.read(input);
int size = Type.VAR_INT.read(input);
byte[] sections = new byte[size];
input.readBytes(sections);
int blockEntities = Type.VAR_INT.read(input);
List<CompoundTag> nbtData = new ArrayList<>();
for (int i = 0; i < blockEntities; i++) {
nbtData.add(Type.NBT.read(input));
}
return new Chunk1_9_3_4(chunkX, chunkZ, groundUp, primaryBitmask, sections, nbtData);
}
@Override
public void write(ByteBuf buffer, Chunk input) throws Exception {
if (!(input instanceof Chunk1_9_3_4))
throw new Exception("Tried to send the wrong chunk type from 1.9.3-4 chunk: " + input.getClass());
Chunk1_9_3_4 chunk = (Chunk1_9_3_4) input;
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
buffer.writeBoolean(chunk.isGroundUp());
Type.VAR_INT.write(buffer, chunk.getBitmask());
Type.VAR_INT.write(buffer, chunk.getSections().length);
buffer.writeBytes(chunk.getSections());
// no block entities as it's earlier
}
}
@@ -0,0 +1,72 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
public class Protocol1_9_1_2TO1_9_3_4 extends Protocol {
public static Type<Chunk> CHUNK = new Chunk1_9_3_4Type();
@Override
protected void registerPackets() {
//Unchanged packet structure
registerOutgoing(State.PLAY, 0x46, 0x47); //Sound effect
registerOutgoing(State.PLAY, 0x47, 0x48); //Player list header and footer
registerOutgoing(State.PLAY, 0x48, 0x49); //Collect item
registerOutgoing(State.PLAY, 0x49, 0x4A); //Entity teleport
registerOutgoing(State.PLAY, 0x4A, 0x4B); //Entity properties
registerOutgoing(State.PLAY, 0x4B, 0x4C); //Entity effect
//Update block entity
registerOutgoing(State.PLAY, 0x09, 0x09, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.POSITION); //Position
map(Type.UNSIGNED_BYTE); //Type
map(Type.NBT); //NBT
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
if (wrapper.get(Type.UNSIGNED_BYTE, 0) == 9) {
Position position = wrapper.get(Type.POSITION, 0);
CompoundTag tag = wrapper.get(Type.NBT, 0);
wrapper.clearPacket(); //Clear the packet
wrapper.setId(0x46); //Update sign packet
wrapper.write(Type.POSITION, position); // Position
for (int i = 1; i < 5; i++)
wrapper.write(Type.STRING, (String) tag.get("Text" + i).getValue()); // Sign line
}
}
});
}
});
registerOutgoing(State.PLAY, 0x20, 0x20, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Chunk chunk = wrapper.passthrough(CHUNK);
BlockEntity.handle(((Chunk1_9_3_4) chunk).getBlockEntities(), wrapper.user());
}
});
}
});
}
@Override
public void init(UserConnection userConnection) {
}
}
@@ -1,7 +1,9 @@
package us.myles.ViaVersion.protocols.protocol1_9_1to1_9;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
@@ -9,7 +11,7 @@ import us.myles.ViaVersion.packets.State;
public class Protocol1_9_1TO1_9 extends Protocol {
@Override
protected void registerPackets() {
// Currently supports 1.9.1 PRE 2
// Currently supports 1.9.1 and 1.9.2
// Join Game Packet
registerOutgoing(State.PLAY, 0x23, 0x23, new PacketRemapper() {
@Override
@@ -24,6 +26,24 @@ public class Protocol1_9_1TO1_9 extends Protocol {
map(Type.BOOLEAN); // 6 - Reduced Debug info
}
});
// Sound Effect Packet
registerOutgoing(State.PLAY, 0x47, 0x47, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Sound ID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int sound = wrapper.get(Type.VAR_INT, 0);
if (sound >= 415) // Add 1 to every sound id since there is no Elytra sound on a 1.9 server
wrapper.set(Type.VAR_INT, 0, sound + 1);
}
});
}
});
}
@Override
@@ -0,0 +1,24 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import java.util.List;
@Data
@AllArgsConstructor
public class Chunk1_9_1_2 implements Chunk {
private int x;
private int z;
private boolean groundUp;
private int bitmask;
private final ChunkSection1_9_1_2[] sections;
private byte[] biomeData;
List<CompoundTag> blockEntities;
public boolean isBiomeData() {
return biomeData != null;
}
}
@@ -0,0 +1,95 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.ChunkSection1_9to1_8;
import java.util.ArrayList;
import java.util.BitSet;
public class Chunk1_9_1_2Type extends BaseChunkType {
public Chunk1_9_1_2Type() {
super("1.9.1/2 Chunk");
}
@Override
public Chunk read(ByteBuf input) throws Exception {
int chunkX = input.readInt();
int chunkZ = input.readInt();
boolean groundUp = input.readBoolean();
int primaryBitmask = Type.VAR_INT.read(input);
int size = Type.VAR_INT.read(input);
BitSet usedSections = new BitSet(16);
ChunkSection1_9_1_2[] sections = new ChunkSection1_9_1_2[16];
// Calculate section count from bitmask
for (int i = 0; i < 16; i++) {
if ((primaryBitmask & (1 << i)) != 0) {
usedSections.set(i);
}
}
// Read sections
for (int i = 0; i < 16; i++) {
if (!usedSections.get(i)) continue; // Section not set
ChunkSection1_9_1_2 section = new ChunkSection1_9_1_2();
sections[i] = section;
// WIP.
// section.readBlocks(input);
// section.readBlockLight(input);
// section.readSkyLight(input);
/*
BlockStorage blocks = new BlockStorage(in);
NibbleArray3d blocklight = new NibbleArray3d(in, 2048); (1024 bytes)
NibbleArray3d skylight = hasSkylight ? new NibbleArray3d(in, 2048) : null; (1024 bytes)
chunks[index] = new Chunk(blocks, blocklight, skylight);
*/
}
byte[] biomeData = groundUp ? new byte[256] : null;
if (groundUp)
input.readBytes(biomeData);
return new Chunk1_9_1_2(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, new ArrayList<CompoundTag>());
}
@Override
public void write(ByteBuf output, Chunk input) throws Exception {
if (!(input instanceof Chunk1_9_1_2))
throw new Exception("Tried to send the wrong chunk type from 1.9.3-4 chunk: " + input.getClass());
Chunk1_9_1_2 chunk = (Chunk1_9_1_2) input;
output.writeInt(chunk.getX());
output.writeInt(chunk.getZ());
output.writeBoolean(chunk.isGroundUp());
Type.VAR_INT.write(output, chunk.getBitmask());
ByteBuf buf = Unpooled.buffer();
for (int i = 0; i < 16; i++) {
ChunkSection1_9_1_2 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);
Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 : 0));
output.writeBytes(buf);
buf.release(); // release buffer
// Write biome data
if (chunk.isBiomeData()) {
output.writeBytes(chunk.getBiomeData());
}
// Type.NBT_ARRAY.write(output, tags.toArray(new CompoundTag[0])); Written by the handler
}
}
@@ -0,0 +1,197 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
public class ChunkSection1_9_1_2 {
/**
* Size (dimensions) of blocks in a chunks section.
*/
public static final int SIZE = 16 * 16 * 16; // width * depth * height
/**
* Length of the sky and block light nibble arrays.
*/
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
/**
* Length of the block data array.
*/
private final List<Integer> palette = Lists.newArrayList();
private final int[] blocks;
private final NibbleArray blockLight;
private NibbleArray skyLight;
public ChunkSection1_9_1_2() {
this.blocks = new int[SIZE];
this.blockLight = new NibbleArray(SIZE);
palette.add(0); // AIR
}
/**
* Set a block in the chunks
*
* @param x Block X
* @param y Block Y
* @param z Block Z
* @param type The type of the block
* @param data The data value of the block
*/
public void setBlock(int x, int y, int z, int type, int data) {
setBlock(index(x, y, z), type, data);
}
public int getBlockId(int x, int y, int z){
int index = blocks[index(x, y, z)];
return palette.indexOf(index) >> 4;
}
/**
* Set a block in the chunks based on the index
*
* @param idx Index
* @param type The type of the block
* @param data The data value of the block
*/
public void setBlock(int idx, int type, int data) {
int hash = type << 4 | (data & 0xF);
int index = palette.indexOf(hash);
if (index == -1) {
index = palette.size();
palette.add(hash);
}
blocks[idx] = index;
}
/**
* Set the block light array
*
* @param data The value to set the block light to
*/
public void setBlockLight(byte[] data) {
blockLight.setHandle(data);
}
/**
* Set the sky light array
*
* @param data The value to set the sky light to
*/
public void setSkyLight(byte[] data) {
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
this.skyLight = new NibbleArray(data);
}
private int index(int x, int y, int z) {
return z << 8 | y << 4 | x;
}
/**
* Write the blocks to a buffer.
*
* @param output The buffer to write to.
* @throws Exception Throws if it failed to write.
*/
public void writeBlocks(ByteBuf output) throws Exception {
// Write bits per block
int bitsPerBlock = 4;
while (palette.size() > 1 << bitsPerBlock) {
bitsPerBlock += 1;
}
long maxEntryValue = (1L << bitsPerBlock) - 1;
output.writeByte(bitsPerBlock);
// Write pallet (or not)
Type.VAR_INT.write(output, palette.size());
for (int mappedId : palette) {
Type.VAR_INT.write(output, mappedId);
}
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
Type.VAR_INT.write(output, length);
long[] data = new long[length];
for (int index = 0; index < blocks.length; index++) {
int value = blocks[index];
int bitIndex = index * bitsPerBlock;
int startIndex = bitIndex / 64;
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
int startBitSubIndex = bitIndex % 64;
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
if (startIndex != endIndex) {
int endBitSubIndex = 64 - startBitSubIndex;
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
}
}
for (long l : data) {
Type.LONG.write(output, l);
}
}
/**
* Write the block light to a buffer
*
* @param output The buffer to write to
*/
public void writeBlockLight(ByteBuf output) {
output.writeBytes(blockLight.getHandle());
}
/**
* Write the sky light to a buffer
*
* @param output The buffer to write to
*/
public void writeSkyLight(ByteBuf output) {
output.writeBytes(skyLight.getHandle());
}
/**
* Check if sky light is present
*
* @return True if skylight is present
*/
public boolean hasSkyLight() {
return skyLight != null;
}
/**
* Get expected size of this chunks section.
*
* @return Amount of bytes sent by this section
* @throws Exception If it failed to calculate bits properly
*/
public int getExpectedSize() throws Exception {
int bitsPerBlock = palette.size() > 255 ? 16 : 8;
int bytes = 1; // bits per block
bytes += paletteBytes(); // palette
bytes += countBytes(bitsPerBlock == 16 ? SIZE * 2 : SIZE); // block data length
bytes += (palette.size() > 255 ? 2 : 1) * SIZE; // block data
bytes += LIGHT_LENGTH; // block light
bytes += hasSkyLight() ? LIGHT_LENGTH : 0; // sky light
return bytes;
}
private int paletteBytes() throws Exception {
// Count bytes used by pallet
int bytes = countBytes(palette.size());
for (int mappedId : palette) {
bytes += countBytes(mappedId);
}
return bytes;
}
private int countBytes(int value) throws Exception {
// Count amount of bytes that would be sent if the value were sent as a VarInt
ByteBuf buf = Unpooled.buffer();
Type.VAR_INT.write(buf, value);
buf.readerIndex(0);
int bitCount = buf.readableBytes();
buf.release();
return bitCount;
}
}
@@ -0,0 +1,74 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2;
import org.bukkit.Material;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import org.spacehq.opennbt.tag.builtin.IntTag;
import org.spacehq.opennbt.tag.builtin.StringTag;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Mojang changed the way how tile entities inside chunk packets work in 1.10.1
* It requires now to have all tile entity data included in the chunk packet, otherwise it'll crash.
*/
public class FakeTileEntity {
private static Map<Integer, CompoundTag> tileEntities = new ConcurrentHashMap<>();
static {
register(Material.FURNACE, "Furnace");
register(Arrays.asList(Material.CHEST, Material.TRAPPED_CHEST), "Chest");
register(Material.ENDER_CHEST, "EnderChest");
register(Material.JUKEBOX, "RecordPlayer");
register(Material.DISPENSER, "Trap");
register(Material.DROPPER, "Dropper");
register(Arrays.asList(Material.SIGN_POST, Material.WALL_SIGN), "Sign");
register(Material.MOB_SPAWNER, "MobSpawner");
register(Material.NOTE_BLOCK, "Music");
register(Material.PISTON_BASE, "Piston");
register(Arrays.asList(Material.BREWING_STAND, Material.CAULDRON), "Cauldron");
register(Material.ENCHANTMENT_TABLE, "EnchantTable");
register(Arrays.asList(Material.ENDER_PORTAL, Material.ENDER_PORTAL_FRAME), "Airportal");
register(Material.BEACON, "Beacon");
register(Arrays.asList(Material.SKULL, Material.SKULL_ITEM), "Skull");
register(Arrays.asList(Material.DAYLIGHT_DETECTOR, Material.DAYLIGHT_DETECTOR_INVERTED), "DLDetector");
register(Material.HOPPER, "Hopper");
register(Arrays.asList(Material.REDSTONE_COMPARATOR, Material.REDSTONE_COMPARATOR_OFF, Material.REDSTONE_COMPARATOR_ON), "Comparator");
register(Material.FLOWER_POT, "FlowerPot");
register(Arrays.asList(Material.STANDING_BANNER, Material.WALL_BANNER, Material.BANNER), "Banner");
register(209, "EndGateway"); // todo test
register(Material.COMMAND.getId(), "Control");
}
private static void register(Integer material, String name) {
CompoundTag comp = new CompoundTag("");
comp.put(new StringTag(name));
tileEntities.put(material, comp);
}
private static void register(Material material, String name) {
register(material.getId(), name);
}
private static void register(List<Material> materials, String name) {
for (Material m : materials)
register(m.getId(), name);
}
public static boolean hasBlock(int block) {
return tileEntities.containsKey(block);
}
public static CompoundTag getFromBlock(int x, int y, int z, int block) {
if (tileEntities.containsKey(block)) {
CompoundTag tag = tileEntities.get(block).clone();
tag.put(new IntTag("x", x));
tag.put(new IntTag("y", y));
tag.put(new IntTag("z", z));
return tag;
}
return null;
}
}
@@ -0,0 +1,116 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import org.spacehq.opennbt.tag.builtin.IntTag;
import org.spacehq.opennbt.tag.builtin.StringTag;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.ChunkSection1_9to1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.ChunkType;
import java.util.ArrayList;
import java.util.List;
public class Protocol1_9_3TO1_9_1_2 extends Protocol {
@Override
protected void registerPackets() {
//Unchanged packet structure
registerOutgoing(State.PLAY, 0x47, 0x46); //Sound effect
registerOutgoing(State.PLAY, 0x48, 0x47); //Player list header and footer
registerOutgoing(State.PLAY, 0x49, 0x48); //Collect item
registerOutgoing(State.PLAY, 0x4A, 0x49); //Entity teleport
registerOutgoing(State.PLAY, 0x4B, 0x4A); //Entity properties
registerOutgoing(State.PLAY, 0x4C, 0x4B); //Entity effect
// Sign update packet
registerOutgoing(State.PLAY, 0x46, -1, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
//read data
Position position = wrapper.read(Type.POSITION);
String[] lines = new String[4];
for (int i = 0; i < 4; i++)
lines[i] = wrapper.read(Type.STRING);
wrapper.clearInputBuffer();
//write data
wrapper.setId(0x09); //Update block entity
wrapper.write(Type.POSITION, position); //Block location
wrapper.write(Type.UNSIGNED_BYTE, (short) 9); //Action type (9 update sign)
//Create nbt
CompoundTag tag = new CompoundTag("");
tag.put(new StringTag("id", "Sign"));
tag.put(new IntTag("x", position.getX().intValue()));
tag.put(new IntTag("y", position.getY().intValue()));
tag.put(new IntTag("z", position.getZ().intValue()));
for (int i = 0; i < lines.length; i++)
tag.put(new StringTag("Text" + (i + 1), lines[i]));
wrapper.write(Type.NBT, tag);
}
});
}
});
// Chunk packet
registerOutgoing(State.PLAY, 0x20, 0x20, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientChunks clientChunks = wrapper.user().get(ClientChunks.class);
ChunkType type = new ChunkType(clientChunks);
if (wrapper.isReadable(type, 0)) {
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.read(type);
List<CompoundTag> tags = new ArrayList<>();
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection1_9to1_8 section = chunk.getSections()[i];
if (section == null)
continue;
for (int x = 0; x < 16; x++)
for (int y = 0; y < 16; y++)
for (int z = 0; z < 16; z++) {
int block = section.getBlockId(x, y, z);
if (FakeTileEntity.hasBlock(block)) {
// NOT SURE WHY Y AND Z WORK THIS WAY, TODO: WORK OUT WHY THIS IS OR FIX W/E BROKE IT
tags.add(FakeTileEntity.getFromBlock(x + (chunk.getX() << 4), z + (i << 4), y + (chunk.getZ() << 4), block));
}
}
}
wrapper.write(type, chunk);
wrapper.write(Type.NBT_ARRAY, tags.toArray(new CompoundTag[0]));
} else {
wrapper.passthroughAll();
wrapper.write(Type.VAR_INT, 0);
}
}
});
}
});
}
@Override
public void init(UserConnection userConnection) {
}
}
@@ -2,17 +2,17 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.MovementTracker;
public class PlayerMovementMapper extends PacketRemapper {
public class PlayerMovementMapper extends PacketHandler {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
wrapper.user().get(MovementTracker.class).incrementIdlePacket();
}
});
public void handle(PacketWrapper wrapper) throws Exception {
MovementTracker tracker = wrapper.user().get(MovementTracker.class);
tracker.incrementIdlePacket();
// If packet has the ground data
if (wrapper.is(Type.BOOLEAN, 0)) {
tracker.setGround(wrapper.get(Type.BOOLEAN, 0));
}
}
}
@@ -4,26 +4,19 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import us.myles.ViaVersion.ViaIdleThread;
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.minecraft.item.Item;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.ValueTransformer;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners.ArmorListener;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners.BlockListener;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners.CommandBlockListener;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners.PaperPatch;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.packets.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.InventoryTracker;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.MovementTracker;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.MetadataListType;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.MetadataType;
@@ -65,23 +58,27 @@ public class Protocol1_9TO1_8 extends Protocol {
return line;
}
public static ItemStack getHandItem(final UserConnection info) {
try {
return Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<ItemStack>() {
@Override
public ItemStack call() throws Exception {
UUID playerUUID = info.get(ProtocolInfo.class).getUuid();
if (Bukkit.getPlayer(playerUUID) != null) {
return Bukkit.getPlayer(playerUUID).getItemInHand();
public static Item getHandItem(final UserConnection info) {
if (HandItemCache.CACHE) {
return HandItemCache.getHandItem(info.get(ProtocolInfo.class).getUuid());
} else {
try {
return Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<Item>() {
@Override
public Item call() throws Exception {
UUID playerUUID = info.get(ProtocolInfo.class).getUuid();
if (Bukkit.getPlayer(playerUUID) != null) {
return Item.getItem(Bukkit.getPlayer(playerUUID).getItemInHand());
}
return null;
}
return null;
}
}).get(10, TimeUnit.SECONDS);
} catch (Exception e) {
System.out.println("Error fetching hand item: " + e.getClass().getName());
if (ViaVersion.getInstance().isDebug())
e.printStackTrace();
return null;
}).get(10, TimeUnit.SECONDS);
} catch (Exception e) {
System.out.println("Error fetching hand item: " + e.getClass().getName());
if (ViaVersion.getInstance().isDebug())
e.printStackTrace();
return null;
}
}
}
@@ -99,13 +96,18 @@ public class Protocol1_9TO1_8 extends Protocol {
ViaVersionPlugin plugin = (ViaVersionPlugin) Bukkit.getPluginManager().getPlugin("ViaVersion");
Bukkit.getPluginManager().registerEvents(new ArmorListener(plugin), plugin);
Bukkit.getPluginManager().registerEvents(new CommandBlockListener(plugin), plugin);
Bukkit.getPluginManager().registerEvents(new DeathListener(plugin), plugin);
Bukkit.getPluginManager().registerEvents(new BlockListener(plugin), plugin);
if(Bukkit.getVersion().toLowerCase().contains("paper")){
plugin.getLogger().info("Enabling PaperSpigot patch: Fixes block placement.");
if (Bukkit.getVersion().toLowerCase().contains("paper") || Bukkit.getVersion().toLowerCase().contains("taco")) {
plugin.getLogger().info("Enabling PaperSpigot/TacoSpigot patch: Fixes block placement.");
Bukkit.getPluginManager().registerEvents(new PaperPatch(), plugin);
}
if (plugin.getConfig().getBoolean("simulate-pt", true))
new ViaIdleThread(plugin.getPortedPlayers()).runTaskTimerAsynchronously(plugin, 1L, 1L); // Updates player's idle status
if (plugin.getConf().isStimulatePlayerTick())
new ViaIdleThread(plugin.getPortedPlayers()).runTaskTimer(plugin, 1L, 1L); // Updates player's idle status
if (plugin.getConf().isItemCache()) {
new HandItemCache().runTaskTimerAsynchronously(plugin, 2L, 2L); // Updates player's items :)
HandItemCache.CACHE = true;
}
}
@Override
@@ -128,5 +130,7 @@ public class Protocol1_9TO1_8 extends Protocol {
userConnection.put(new MovementTracker(userConnection));
// Inventory tracker
userConnection.put(new InventoryTracker(userConnection));
// Place block tracker
userConnection.put(new PlaceBlockTracker(userConnection));
}
}
@@ -0,0 +1,113 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8;
import io.netty.channel.ChannelHandlerContext;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.MovementTracker;
import us.myles.ViaVersion.util.PipelineUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
public class ViaIdleThread extends BukkitRunnable {
private static boolean USE_NMS = true;
private final Map<UUID, UserConnection> portedPlayers;
// Used for packet mode
private Object idlePacket;
private Object idlePacket2;
// Use for nms
private Method getHandle;
private Field connection;
private Method handleFlying;
public ViaIdleThread(Map<UUID, UserConnection> portedPlayers) {
USE_NMS = ViaVersion.getConfig().isNMSPlayerTicking();
this.portedPlayers = portedPlayers;
Class<?> idlePacketClass;
try {
idlePacketClass = ReflectionUtil.nms("PacketPlayInFlying");
} catch (ClassNotFoundException e) {
throw new RuntimeException("Couldn't find idle packet, help!", e);
}
try {
idlePacket = idlePacketClass.newInstance();
idlePacket2 = idlePacketClass.newInstance();
Field flying = idlePacketClass.getDeclaredField("f");
flying.setAccessible(true);
flying.set(idlePacket2, true);
} catch (NoSuchFieldException | InstantiationException | IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("Couldn't make player idle packet, help!", e);
}
if (USE_NMS) {
try {
getHandle = ReflectionUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle");
} catch (NoSuchMethodException | ClassNotFoundException e) {
throw new RuntimeException("Couldn't find CraftPlayer", e);
}
try {
connection = ReflectionUtil.nms("EntityPlayer").getDeclaredField("playerConnection");
} catch (NoSuchFieldException | ClassNotFoundException e) {
throw new RuntimeException("Couldn't find Player Connection", e);
}
try {
handleFlying = ReflectionUtil.nms("PlayerConnection").getDeclaredMethod("a", idlePacketClass);
} catch (NoSuchMethodException | ClassNotFoundException e) {
throw new RuntimeException("Couldn't find CraftPlayer", e);
}
}
}
@Override
public void run() {
for (UserConnection info : portedPlayers.values()) {
if (info.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) {
long nextIdleUpdate = info.get(MovementTracker.class).getNextIdlePacket();
if (nextIdleUpdate <= System.currentTimeMillis()) {
if (info.getChannel().isOpen()) {
if (USE_NMS) {
Player player = Bukkit.getPlayer(info.get(ProtocolInfo.class).getUuid());
if (player != null) {
try {
// Tick player
Object entityPlayer = getHandle.invoke(player);
Object pc = connection.get(entityPlayer);
if (pc != null) {
handleFlying.invoke(pc, (info.get(MovementTracker.class).isGround() ? idlePacket2 : idlePacket));
// Tick world
info.get(MovementTracker.class).incrementIdlePacket();
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
} else {
// Old method using packets.
ChannelHandlerContext context = PipelineUtil.getContextBefore("decoder", info.getChannel().pipeline());
if (context != null) {
if (info.get(MovementTracker.class).isGround()) {
context.fireChannelRead(idlePacket2);
} else {
context.fireChannelRead(idlePacket);
}
info.get(MovementTracker.class).incrementIdlePacket();
}
}
}
}
}
}
}
}
@@ -0,0 +1,25 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.chat;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker;
public class ChatRewriter {
public static void toClient(JsonObject obj, UserConnection user) {
//Check gamemode change
if (obj.get("translate") != null && obj.get("translate").getAsString().equals("gameMode.changed")) {
String gameMode = user.get(EntityTracker.class).getGameMode().getText();
JsonObject gameModeObject = new JsonObject();
gameModeObject.addProperty("text", gameMode);
gameModeObject.addProperty("color", "gray");
gameModeObject.addProperty("italic", true);
JsonArray array = new JsonArray();
array.add(gameModeObject);
obj.add("with", array);
}
}
}
@@ -0,0 +1,23 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.chat;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter
public enum GameMode {
SURVIVAL(0, "Survival Mode"),
CREATIVE(1, "Creative Mode"),
ADVENTURE(2, "Adventure Mode"),
SPECTATOR(3, "Spectator Mode");
private final int id;
private final String text;
public static GameMode getById(int id) {
for (GameMode gm : GameMode.values())
if (gm.getId() == id)
return gm;
return null;
}
}
@@ -0,0 +1,39 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
@RequiredArgsConstructor
@Getter
@ToString
public class Chunk1_9to1_8 implements Chunk {
private final int x;
private final int z;
private final boolean groundUp;
private final int primaryBitmask;
private final ChunkSection1_9to1_8[] sections;
private final byte[] biomeData;
private boolean unloadPacket = false;
/**
* Chunk unload.
*
* @param x coord
* @param z coord
*/
public Chunk1_9to1_8(int x, int z) {
this(x, z, true, 0, new ChunkSection1_9to1_8[16], null);
this.unloadPacket = true;
}
/**
* Does this chunks have biome data
*
* @return True if the chunks has biome data
*/
public boolean hasBiomeData() {
return biomeData != null && groundUp;
}
}
@@ -1,15 +1,16 @@
package us.myles.ViaVersion.api.minecraft.chunks;
package us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
public class ChunkSection {
public class ChunkSection1_9to1_8 {
/**
* Size (dimensions) of blocks in a chunk section.
* Size (dimensions) of blocks in a chunks section.
*/
public static final int SIZE = 16 * 16 * 16; // width * depth * height
/**
@@ -25,14 +26,14 @@ public class ChunkSection {
private final NibbleArray blockLight;
private NibbleArray skyLight;
public ChunkSection() {
public ChunkSection1_9to1_8() {
this.blocks = new int[SIZE];
this.blockLight = new NibbleArray(SIZE);
palette.add(0); // AIR
}
/**
* Set a block in the chunk
* Set a block in the chunks
*
* @param x Block X
* @param y Block Y
@@ -44,8 +45,13 @@ public class ChunkSection {
setBlock(index(x, y, z), type, data);
}
public int getBlockId(int x, int y, int z){
int index = blocks[index(x, y, z)];
return palette.get(index) >> 4;
}
/**
* Set a block in the chunk based on the index
* Set a block in the chunks based on the index
*
* @param idx Index
* @param type The type of the block
@@ -154,9 +160,10 @@ public class ChunkSection {
}
/**
* Get expected size of this chunk section.
* Get expected size of this chunks section.
*
* @return Amount of bytes sent by this section
* @throws Exception If it failed to calculate bits properly
*/
public int getExpectedSize() throws Exception {
int bitsPerBlock = palette.size() > 255 ? 16 : 8;
@@ -1,7 +1,5 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity;
@@ -17,6 +15,7 @@ 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.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type;
@@ -39,20 +38,21 @@ public class ArmorListener implements Listener {
if (!userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
int armor = ArmorType.calculateArmorPoints(player.getInventory().getArmorContents());
try {
ByteBuf buf = Unpooled.buffer();
Type.VAR_INT.write(buf, 0x4B); // Entity Properties
Type.VAR_INT.write(buf, player.getEntityId());
buf.writeInt(1); // only 1 property
Type.STRING.write(buf, "generic.armor");
buf.writeDouble(0); //default 0 armor
Type.VAR_INT.write(buf, 1); // 1 modifier
Type.UUID.write(buf, ARMOR_ATTRIBUTE); // 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);
} catch (Exception ignored) {
PacketWrapper wrapper = new PacketWrapper(0x4B, null, userConnection);
try {
wrapper.write(Type.VAR_INT, player.getEntityId()); // Player ID
wrapper.write(Type.INT, 1); // only 1 property
wrapper.write(Type.STRING, "generic.armor");
wrapper.write(Type.DOUBLE, 0D); //default 0 armor
wrapper.write(Type.VAR_INT, 1); // 1 modifier
wrapper.write(Type.UUID, ARMOR_ATTRIBUTE); // armor modifier uuid
wrapper.write(Type.DOUBLE, (double) armor); // the modifier value
wrapper.write(Type.BYTE, (byte) 0);// the modifier operation, 0 is add number
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -20,7 +20,7 @@ public class BlockListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void placeBlock(BlockPlaceEvent e) {
if(plugin.isPorted(e.getPlayer())) {
if (plugin.isPorted(e.getPlayer())) {
UserConnection c = plugin.getConnection(e.getPlayer());
if (!c.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
Block b = e.getBlockPlaced();
@@ -19,6 +19,7 @@ import org.bukkit.event.player.PlayerRespawnEvent;
import org.spacehq.opennbt.tag.builtin.ByteTag;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
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.minecraft.Position;
@@ -80,12 +81,12 @@ public class CommandBlockListener implements Listener {
if (!userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
try {
ByteBuf buf = Unpooled.buffer();
Type.VAR_INT.write(buf, 0x1B); // Entity Status
buf.writeInt(p.getEntityId());
buf.writeByte(26);
plugin.sendRawPacket(p, buf);
} catch (Exception ignored) {
PacketWrapper wrapper = new PacketWrapper(0x1B, null, userConnection); // Entity status
wrapper.write(Type.INT, p.getEntityId());
wrapper.write(Type.BYTE, (byte) 26); //Hardcoded op permission level
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -97,36 +98,46 @@ public class CommandBlockListener implements Listener {
Object tileEntityCommand = ReflectionUtil.get(cmd, "commandBlock", ReflectionUtil.nms("TileEntityCommand"));
Object updatePacket = ReflectionUtil.invoke(tileEntityCommand, "getUpdatePacket");
ByteBuf buf = packetToByteBuf(updatePacket);
plugin.sendRawPacket(player, buf);
UserConnection userConnection = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(player);
PacketWrapper wrapper = generatePacket(updatePacket, userConnection);
wrapper.send(Protocol1_9TO1_8.class);
}
private ByteBuf packetToByteBuf(Object updatePacket) throws Exception {
ByteBuf buf = Unpooled.buffer();
Type.VAR_INT.write(buf, 0x09); //Block Entity Packet ID
private PacketWrapper generatePacket(Object updatePacket, UserConnection usr) throws Exception {
PacketWrapper wrapper = new PacketWrapper(0x09, null, usr); // Update block entity
long[] pos = getPosition(ReflectionUtil.get(updatePacket, "a", ReflectionUtil.nms("BlockPosition")));
Type.POSITION.write(buf, new Position(pos[0], pos[1], pos[2])); //Block position
buf.writeByte(2); //Action id always 2
wrapper.write(Type.POSITION, new Position(pos[0], pos[1], pos[2])); //Block position
wrapper.write(Type.BYTE, (byte) 2); // Action id always 2
CompoundTag nbt = getNBT(ReflectionUtil.get(updatePacket, "c", ReflectionUtil.nms("NBTTagCompound")));
if (nbt == null) {
buf.writeByte(0); //If nbt is null. Use 0 as nbt
return buf;
wrapper.write(Type.BYTE, (byte) 0); //If nbt is null. Use 0 as nbt
return wrapper;
}
nbt.put(new ByteTag("powered", (byte) 0));
nbt.put(new ByteTag("auto", (byte) 0));
nbt.put(new ByteTag("conditionMet", (byte) 0));
Type.NBT.write(buf, nbt); //NBT tag
return buf;
wrapper.write(Type.NBT, nbt); // NBT TAG
return wrapper;
}
private long[] getPosition(Object obj) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
return new long[]{
(long) ReflectionUtil.getSuper(obj, "a", int.class), //X
(long) ReflectionUtil.getSuper(obj, "c", int.class), //Y
(long) ReflectionUtil.getSuper(obj, "d", int.class) //Z
(long) ReflectionUtil.getSuper(obj, isR1() ? "b" : "c", int.class), //Y
(long) ReflectionUtil.getSuper(obj, isR1() ? "c" : "d", int.class) //Z
};
}
private boolean isR1() {
return ReflectionUtil.getVersion().equals("v1_8_R1");
}
private CompoundTag getNBT(Object obj) throws Exception {
ByteBuf buf = Unpooled.buffer();
Method m = ReflectionUtil.nms("NBTCompressedStreamTools").getMethod("a", ReflectionUtil.nms("NBTTagCompound"), DataOutput.class);
@@ -0,0 +1,66 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
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.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
@RequiredArgsConstructor
public class DeathListener implements Listener {
private final ViaVersionPlugin plugin;
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onDeath(PlayerDeathEvent e) {
Player p = e.getEntity();
if (ViaVersion.getConfig().isShowNewDeathMessages() && checkGamerule(p.getWorld()) && e.getDeathMessage() != null && checkPipeline(p)) {
sendPacket(p, e.getDeathMessage());
}
}
public boolean checkPipeline(Player p) {
UserConnection userConnection = plugin.getConnection(p);
return userConnection != null && userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class);
}
private UserConnection getUserConnection(Player p) {
return plugin.getConnection(p);
}
public boolean checkGamerule(World w) {
try {
return Boolean.parseBoolean(w.getGameRuleValue("showDeathMessages"));
} catch (Exception e) {
return false;
}
}
private void sendPacket(final Player p, final String msg) {
Bukkit.getScheduler().runTask(plugin, new Runnable() {
@Override
public void run() {
PacketWrapper wrapper = new PacketWrapper(0x2C, null, getUserConnection(p));
try {
wrapper.write(Type.VAR_INT, 2);
wrapper.write(Type.VAR_INT, p.getEntityId());
wrapper.write(Type.INT, p.getEntityId());
Protocol1_9TO1_8.FIX_JSON.write(wrapper, msg);
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
wrapper.clearInputBuffer();
}
}
});
}
}
@@ -0,0 +1,36 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import us.myles.ViaVersion.api.minecraft.item.Item;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class HandItemCache extends BukkitRunnable {
public static boolean CACHE = false;
private static ConcurrentHashMap<UUID, Item> handCache = new ConcurrentHashMap<>();
public static Item getHandItem(UUID player) {
if (!handCache.containsKey(player))
return null;
return handCache.get(player);
}
@Override
public void run() {
List<UUID> players = new ArrayList<>(handCache.keySet());
for (Player p : Bukkit.getOnlinePlayers()) {
handCache.put(p.getUniqueId(), Item.getItem(p.getItemInHand()));
players.remove(p.getUniqueId());
}
// Remove offline players
for (UUID uuid : players) {
handCache.remove(uuid);
}
}
}
@@ -32,18 +32,18 @@ public class PaperPatch implements Listener {
} else {
if (e.getPlayer().getLocation().getBlock().getRelative(BlockFace.UP).equals(e.getBlock())) {
e.setCancelled(true);
}else{
} else {
// Within radius of block
if(Math.abs(diff.getX()) <= 0.8 && Math.abs(diff.getZ()) <= 0.8D){
if (Math.abs(diff.getX()) <= 0.8 && Math.abs(diff.getZ()) <= 0.8D) {
// Are they on the edge / shifting ish
if(diff.getY() <= 0.1D && diff.getY() >= -0.1D){
if (diff.getY() <= 0.1D && diff.getY() >= -0.1D) {
e.setCancelled(true);
return;
}
BlockFace relative = e.getBlockAgainst().getFace(e.getBlock());
// Are they towering up, (handles some latency)
if(relative == BlockFace.UP){
if(diff.getY() < 1D && diff.getY() >= 0D){
if (relative == BlockFace.UP) {
if (diff.getY() < 1D && diff.getY() >= 0D) {
e.setCancelled(true);
}
}
@@ -157,7 +157,7 @@ public enum MetaIndex {
}
public static MetaIndex getIndex(EntityType type, int index) {
Class<? extends org.bukkit.entity.Entity> entityClass = type.getEntityClass();
Class<? extends org.bukkit.entity.Entity> entityClass = type == null ? LivingEntity.class : type.getEntityClass();
if (entityClass == null) {
System.out.println("Could not get entity class for " + type);
return null;
@@ -3,7 +3,6 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata;
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.api.minecraft.item.Item;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
@@ -2,25 +2,31 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import us.myles.ViaVersion.api.type.Type;
@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);
Byte(0, Type.BYTE),
VarInt(1, Type.VAR_INT),
Float(2, Type.FLOAT),
String(3, Type.STRING),
Chat(4, Type.STRING),
Slot(5, Type.ITEM),
Boolean(6, Type.BOOLEAN),
Vector3F(7, Type.ROTATION),
Position(8, Type.POSITION),
OptPosition(9, Type.OPTIONAL_POSITION),
Direction(10, Type.VAR_INT),
OptUUID(11, Type.OPTIONAL_UUID),
BlockID(12, Type.VAR_INT),
Discontinued(99, null);
private final int typeID;
private final Type type;
public static NewType byId(int id) {
return values()[id];
}
}
@@ -57,7 +57,7 @@ public class EntityPackets {
passengerPacket.write(Type.VAR_INT_ARRAY, new Integer[]{passenger});
tracker.getVehicleMap().put(passenger, vehicle);
}
passengerPacket.send(); // Send the packet
passengerPacket.send(Protocol1_9TO1_8.class); // Send the packet
}
return null;
}
@@ -184,9 +184,10 @@ public class EntityPackets {
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
if (tracker.getClientEntityTypes().containsKey(entityID)) {
MetadataRewriter.transform(tracker.getClientEntityTypes().get(entityID), metadataList);
} else if (!ViaVersion.getConfig().isUnknownEntitiesSuppressed() || ViaVersion.getInstance().isDebug()) {
System.out.println("Unable to find entity for metadata, entity ID: " + entityID);
metadataList.clear();
} else {
// Buffer
tracker.addMetadataToBuffer(entityID, metadataList);
wrapper.cancel();
}
}
});
@@ -207,8 +208,9 @@ public class EntityPackets {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
if(metadataList.size() == 0)
if (metadataList.size() == 0) {
wrapper.cancel();
}
}
});
}
@@ -243,15 +245,30 @@ public class EntityPackets {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
// int id = wrapper.read(Type.VAR_INT);
// CompoundTag tag = wrapper.read(Type.NBT);
// System.out.println(id + " - " + tag);
wrapper.cancel();
}
});
}
});
// Combat Event Packet
protocol.registerOutgoing(State.PLAY, 0x42, 0x2C, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); //Event id
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
if (wrapper.get(Type.VAR_INT, 0) == 2) { // entity dead
wrapper.passthrough(Type.VAR_INT); //Player id
wrapper.passthrough(Type.INT); //Entity id
Protocol1_9TO1_8.FIX_JSON.write(wrapper, wrapper.read(Type.STRING));
}
}
});
}
});
/* Packets which do not have any field remapping or handlers */
@@ -261,7 +278,6 @@ public class EntityPackets {
protocol.registerOutgoing(State.PLAY, 0x16, 0x27); // Entity Look Packet
protocol.registerOutgoing(State.PLAY, 0x14, 0x28); // Entity Packet
protocol.registerOutgoing(State.PLAY, 0x42, 0x2C); // Combat Event Packet
protocol.registerOutgoing(State.PLAY, 0x0A, 0x2F); // Use Bed Packet
protocol.registerOutgoing(State.PLAY, 0x1E, 0x31); // Remove Entity Effect Packet
@@ -44,7 +44,7 @@ public class InventoryPackets {
wrapper.write(Type.SHORT, property);
wrapper.write(Type.SHORT, enchantID);
}
}).send();
}).send(Protocol1_9TO1_8.class);
wrapper.set(Type.SHORT, 0, (short) (property + 3));
wrapper.set(Type.SHORT, 1, level);
@@ -146,10 +146,10 @@ public class InventoryPackets {
Item[] oldStack = wrapper.get(Type.ITEM_ARRAY, 0);
Item[] newStack = new Item[oldStack.length + 1];
for (int i = 0; i < newStack.length; i++) {
if (i > 3) {
if (i > 4) {
newStack[i] = oldStack[i - 1];
} else {
if (i != 3) { // Leave index 3 blank
if (i != 4) { // Leave index 3 blank
newStack[i] = oldStack[i];
}
}
@@ -230,7 +230,7 @@ public class InventoryPackets {
wrapper.write(Type.SHORT, slot);
wrapper.write(Type.SHORT, (short) -1);
}
}).send();
}).send(Protocol1_9TO1_8.class);
// Finally reset to simulate throwing item
wrapper.set(Type.SHORT, 0, (short) -999); // Set slot to -999
}
@@ -285,7 +285,7 @@ public class InventoryPackets {
wrapper.write(Type.SHORT, slot);
wrapper.write(Type.SHORT, (short) -1);
}
}).send();
}).send(Protocol1_9TO1_8.class);
// Finally reset to simulate throwing item
wrapper.set(Type.BYTE, 0, (byte) 0); // Set button to 0
wrapper.set(Type.BYTE, 1, (byte) 0); // Set mode to 0
@@ -1,5 +1,7 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.packets;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import us.myles.ViaVersion.ViaVersionPlugin;
@@ -13,8 +15,11 @@ import us.myles.ViaVersion.api.remapper.ValueCreator;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.ItemRewriter;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.PlayerMovementMapper;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chat.ChatRewriter;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chat.GameMode;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker;
@@ -26,6 +31,19 @@ public class PlayerPackets {
public void registerMap() {
map(Type.STRING, Protocol1_9TO1_8.FIX_JSON); // 0 - Chat Message (json)
map(Type.BYTE); // 1 - Chat Positon
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
try {
JsonObject obj = (JsonObject) new JsonParser().parse(wrapper.get(Type.STRING, 0));
ChatRewriter.toClient(obj, wrapper.user());
wrapper.set(Type.STRING, 0, obj.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
});
@@ -159,6 +177,14 @@ public class PlayerPackets {
map(Type.UNSIGNED_BYTE); // 4 - Max Players (Tab)
map(Type.STRING); // 5 - Level Type
map(Type.BOOLEAN); // 6 - Reduced Debug info
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.setGameMode(GameMode.getById(wrapper.get(Type.UNSIGNED_BYTE, 0))); //Set player gamemode
}
});
}
});
@@ -228,6 +254,31 @@ public class PlayerPackets {
wrapper.passthrough(Type.REMAINING_BYTES); // This is so ugly, :(
wrapper.write(Type.VAR_INT, 0);
}
if (name.equalsIgnoreCase("MC|TrList")) {
wrapper.passthrough(Type.INT); // ID
Short size = wrapper.passthrough(Type.UNSIGNED_BYTE);
for (int i = 0; i < size; ++i) {
Item item1 = wrapper.passthrough(Type.ITEM);
ItemRewriter.toClient(item1);
Item item2 = wrapper.passthrough(Type.ITEM);
ItemRewriter.toClient(item2);
boolean present = wrapper.passthrough(Type.BOOLEAN);
if (present) {
Item item3 = wrapper.passthrough(Type.ITEM);
ItemRewriter.toClient(item3);
}
wrapper.passthrough(Type.BOOLEAN);
wrapper.passthrough(Type.INT);
wrapper.passthrough(Type.INT);
}
}
}
});
}
@@ -253,6 +304,49 @@ public class PlayerPackets {
}
});
// Respawn Packet
protocol.registerOutgoing(State.PLAY, 0x07, 0x33, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Dimension
map(Type.UNSIGNED_BYTE); // 1 - Difficulty
map(Type.UNSIGNED_BYTE); // 2 - GameMode
map(Type.STRING); // 3 - Level Type
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
// Client unloads chunks on respawn, take note
ClientChunks cc = wrapper.user().get(ClientChunks.class);
cc.getBulkChunks().clear();
cc.getLoadedChunks().clear();
int gamemode = wrapper.get(Type.UNSIGNED_BYTE, 0);
wrapper.user().get(EntityTracker.class).setGameMode(GameMode.getById(gamemode));
}
});
}
});
// Change Game State Packet
protocol.registerOutgoing(State.PLAY, 0x2B, 0x1E, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.UNSIGNED_BYTE); //0 - Reason
map(Type.FLOAT); //1 - Value
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
if (wrapper.get(Type.UNSIGNED_BYTE, 0) == 3) { //Change gamemode
int gamemode = wrapper.get(Type.FLOAT, 0).intValue();
wrapper.user().get(EntityTracker.class).setGameMode(GameMode.getById(gamemode));
}
}
});
}
});
/* Removed packets */
// Map Bulk
@@ -290,9 +384,7 @@ public class PlayerPackets {
protocol.registerOutgoing(State.PLAY, 0x39, 0x2B); // Player Abilities Packet
protocol.registerOutgoing(State.PLAY, 0x00, 0x1F); // Keep Alive Packet
protocol.registerOutgoing(State.PLAY, 0x48, 0x32); // Resource Pack Send Packet
protocol.registerOutgoing(State.PLAY, 0x07, 0x33); // Respawn Packet
protocol.registerOutgoing(State.PLAY, 0x43, 0x36); // Camera Packet
protocol.registerOutgoing(State.PLAY, 0x2B, 0x1E); // Change Game State Packet
protocol.registerOutgoing(State.PLAY, 0x3D, 0x38); // Display Scoreboard Packet
protocol.registerOutgoing(State.PLAY, 0x3B, 0x3F); // Scoreboard Objective Packet
@@ -428,6 +520,52 @@ public class PlayerPackets {
}
});
// Player Position Packet
protocol.registerIncoming(State.PLAY, 0x04, 0x0C, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.DOUBLE); // 0 - X
map(Type.DOUBLE); // 1 - Y
map(Type.DOUBLE); // 2 - Z
map(Type.BOOLEAN); // 3 - Ground
handler(new PlayerMovementMapper());
}
});
// Player Move & Look Packet
protocol.registerIncoming(State.PLAY, 0x06, 0x0D, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.DOUBLE); // 0 - X
map(Type.DOUBLE); // 1 - Y
map(Type.DOUBLE); // 2 - Z
map(Type.FLOAT); // 3 - Yaw
map(Type.FLOAT); // 4 - Pitch
map(Type.BOOLEAN); // 5 - Ground
handler(new PlayerMovementMapper());
}
});
// Player Look Packet
protocol.registerIncoming(State.PLAY, 0x05, 0x0E, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.FLOAT); // 0 - Yaw
map(Type.FLOAT); // 1 - Pitch
map(Type.BOOLEAN); // 2 - Ground
handler(new PlayerMovementMapper());
}
});
// Player Packet
protocol.registerIncoming(State.PLAY, 0x03, 0x0F, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.BOOLEAN); // 0 - Ground
handler(new PlayerMovementMapper());
}
});
/* Packets which do not have any field remapping or handlers */
protocol.registerIncoming(State.PLAY, 0x01, 0x02); // Chat Message Packet
@@ -436,9 +574,6 @@ public class PlayerPackets {
protocol.registerIncoming(State.PLAY, 0x00, 0x0B); // Keep Alive Request Packet
protocol.registerIncoming(State.PLAY, 0x04, 0x0C, new PlayerMovementMapper()); // Player Position Packet
protocol.registerIncoming(State.PLAY, 0x06, 0x0D, new PlayerMovementMapper()); // Player Move & Look Packet
protocol.registerIncoming(State.PLAY, 0x05, 0x0E, new PlayerMovementMapper()); // Player Look Packet
protocol.registerIncoming(State.PLAY, 0x03, 0x0F, new PlayerMovementMapper()); // Player Packet
}
}
@@ -55,6 +55,7 @@ public class SpawnPackets {
int typeID = wrapper.get(Type.BYTE, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.getClientEntityTypes().put(entityID, EntityUtil.getTypeFromID(typeID, true));
tracker.sendMetadataBuffer(entityID);
}
});
@@ -103,13 +104,13 @@ public class SpawnPackets {
List<Metadata> meta = new ArrayList<>();
Item item = new Item((short) Material.POTION.getId(), (byte) 1, (short) data, null);
ItemRewriter.toClient(item); // Rewrite so that it gets the right nbt
// TEMP FIX FOR POTIONS UNTIL WE FIGURE OUT HOW TO TRANSFORM SENT PACKETS
Metadata potion = new Metadata(5, NewType.Slot.getTypeID(), Type.ITEM, item);
meta.add(potion);
wrapper.write(Protocol1_9TO1_8.METADATA_LIST, meta);
}
});
metaPacket.send();
metaPacket.send(Protocol1_9TO1_8.class);
}
}
});
@@ -129,6 +130,7 @@ public class SpawnPackets {
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.getClientEntityTypes().put(entityID, EntityType.EXPERIENCE_ORB);
tracker.sendMetadataBuffer(entityID);
}
});
@@ -154,6 +156,7 @@ public class SpawnPackets {
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.getClientEntityTypes().put(entityID, EntityType.LIGHTNING);
tracker.sendMetadataBuffer(entityID);
}
});
@@ -187,6 +190,7 @@ public class SpawnPackets {
int typeID = wrapper.get(Type.UNSIGNED_BYTE, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.getClientEntityTypes().put(entityID, EntityUtil.getTypeFromID(typeID, false));
tracker.sendMetadataBuffer(entityID);
}
});
@@ -243,6 +247,7 @@ public class SpawnPackets {
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.getClientEntityTypes().put(entityID, EntityType.PAINTING);
tracker.sendMetadataBuffer(entityID);
}
});
@@ -276,6 +281,7 @@ public class SpawnPackets {
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.getClientEntityTypes().put(entityID, EntityType.PLAYER);
tracker.sendMetadataBuffer(entityID);
}
});
@@ -294,10 +300,10 @@ public class SpawnPackets {
if (item != 0) {
PacketWrapper packet = new PacketWrapper(0x3C, null, wrapper.user());
packet.write(Type.VAR_INT, wrapper.get(Type.VAR_INT, 0));
packet.write(Type.VAR_INT, 1);
packet.write(Type.VAR_INT, 0);
packet.write(Type.ITEM, new Item(item, (byte) 1, (short) 0, null));
try {
packet.send();
packet.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
@@ -6,7 +6,6 @@ import org.spacehq.opennbt.tag.builtin.StringTag;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
@@ -14,12 +13,14 @@ import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.remapper.ValueCreator;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.ArmorType;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.ItemRewriter;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds.Effect;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds.SoundEffect;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.PlaceBlockTracker;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.ChunkType;
public class WorldPackets {
@@ -49,12 +50,8 @@ public class WorldPackets {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int id = wrapper.get(Type.INT, 0);
if (id >= 1000 && id < 2000 && id != 1005) { // Sound Effect
wrapper.cancel();
}
if (id == 1005) { // Fix jukebox
id = 1010;
}
id = Effect.getNewId(id);
wrapper.set(Type.INT, 0, id);
}
});
@@ -118,14 +115,12 @@ public class WorldPackets {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientChunks clientChunks = wrapper.user().get(ClientChunks.class);
Chunk chunk = wrapper.passthrough(new ChunkType(clientChunks));
if (chunk.isUnloadPacket()) {
PacketWrapper unload = wrapper.create(0x1D);
unload.write(Type.INT, chunk.getX());
unload.write(Type.INT, chunk.getZ());
unload.send();
wrapper.cancel();
}
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.passthrough(new ChunkType(clientChunks));
if (chunk.isUnloadPacket())
wrapper.setId(0x1D);
// eat any other data (Usually happens with unload packets)
wrapper.read(Type.REMAINING_BYTES);
}
});
}
@@ -182,11 +177,19 @@ public class WorldPackets {
});
}
});
// Block Change Packet
protocol.registerOutgoing(State.PLAY, 0x23, 0x0B, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.POSITION);
map(Type.VAR_INT);
}
});
/* Packets which do not have any field remapping or handlers */
protocol.registerOutgoing(State.PLAY, 0x25, 0x08); // Block Break Animation Packet
protocol.registerOutgoing(State.PLAY, 0x24, 0x0A); // Block Action Packet
protocol.registerOutgoing(State.PLAY, 0x23, 0x0B); // Block Change Packet
protocol.registerOutgoing(State.PLAY, 0x22, 0x10); // Multi Block Change Packet
protocol.registerOutgoing(State.PLAY, 0x27, 0x1C); // Explosion Packet
protocol.registerOutgoing(State.PLAY, 0x2A, 0x22); // Particle Packet
@@ -227,7 +230,7 @@ public class WorldPackets {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int status = wrapper.get(Type.UNSIGNED_BYTE, 0);
if (status == 5) {
if (status == 5 || status == 4 || status == 3) {
EntityTracker entityTracker = wrapper.user().get(EntityTracker.class);
if (entityTracker.isBlocking()) {
entityTracker.setBlocking(false);
@@ -254,7 +257,7 @@ public class WorldPackets {
wrapper.write(Type.LONG, -1L);
wrapper.write(Type.BYTE, (byte) 255);
// Write item in hand
Item item = Item.getItem(Protocol1_9TO1_8.getHandItem(wrapper.user()));
Item item = Protocol1_9TO1_8.getHandItem(wrapper.user());
// Blocking patch
if (ViaVersion.getConfig().isShieldBlocking()) {
if (item != null) {
@@ -280,46 +283,6 @@ public class WorldPackets {
wrapper.write(Type.BYTE, (byte) 0);
}
});
/*
The thing i've discovered is when using an item in air, it needs to send 2 packets.
I believe the issue is that this needs to be flipped with the packet above while still
sending block info.
Otherwise no idea, the disadvantage: Interact does not get fired if you right click
special items. (there's quite a few...)
*/
// handler(new PacketHandler() {
// @Override
// public void handle(PacketWrapper wrapper) throws Exception {
// if(wrapper.isCancelled()) return;
// EntityTracker tracker = wrapper.user().get(EntityTracker.class);
// if(tracker.isBlocking()) return;
//
// Long last = tracker.getLastPlaceBlock();
// if (last != -1) {
// if ((wrapper.user().getReceivedPackets() - last) < 3) {
// tracker.setLastPlaceBlock(-1L);
// return;
// }
// tracker.setLastPlaceBlock(-1L);
// }
// final Item item = wrapper.get(Type.ITEM, 0);
// wrapper.create(0x08, new ValueCreator() {
// @Override
// public void write(PacketWrapper wrapper) throws Exception {
// wrapper.write(Type.POSITION, new Position(1L, 1L, 1L));
// wrapper.write(Type.BYTE, (byte) 2);
// wrapper.write(Type.ITEM, item); // hand
//
// wrapper.write(Type.UNSIGNED_BYTE, (short) 1);
// wrapper.write(Type.UNSIGNED_BYTE, (short) 1);
// wrapper.write(Type.UNSIGNED_BYTE, (short) 1);
// }
// }).sendToServer();
// }
// });
}
});
@@ -334,7 +297,7 @@ public class WorldPackets {
create(new ValueCreator() {
@Override
public void write(PacketWrapper wrapper) throws Exception {
Item item = Item.getItem(Protocol1_9TO1_8.getHandItem(wrapper.user()));
Item item = Protocol1_9TO1_8.getHandItem(wrapper.user());
wrapper.write(Type.ITEM, item); // 3 - Item
}
});
@@ -346,32 +309,12 @@ public class WorldPackets {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Item item = wrapper.get(Type.ITEM, 0);
if (item != null) {
Material m = Material.getMaterial(item.getId());
if (m != null) {
// Prevent special items from sending certain info
// Books
boolean special = m == Material.WRITTEN_BOOK;
// Buckets
special = special || m == Material.WATER_BUCKET || m == Material.LAVA_BUCKET || m == Material.BUCKET;
// Food
special = special || m.isEdible();
// Potions
special = special || m == Material.POTION || m == Material.GLASS_BOTTLE;
// Projectiles
special = special || m == Material.BOW;
special = special || m == Material.SNOW_BALL || m == Material.EGG || m == Material.EXP_BOTTLE || m == Material.ENDER_PEARL || m == Material.EYE_OF_ENDER;
// Armour
special = special || ArmorType.isArmor(m);
// Don't send data if special
if (special && m != Material.AIR) {
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.setLastPlaceBlock(wrapper.user().getReceivedPackets());
}
}
}
Position position = wrapper.get(Type.POSITION, 0);
PlaceBlockTracker tracker = wrapper.user().get(PlaceBlockTracker.class);
if (tracker.getLastPlacedPosition() != null && tracker.getLastPlacedPosition().equals(position) && !tracker.isExpired(50))
wrapper.cancel();
tracker.updateTime();
tracker.setLastPlacedPosition(position);
}
});
@@ -0,0 +1,44 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds;
import java.util.HashMap;
public class Effect {
private static HashMap<Integer, Integer> effects;
static {
effects = new HashMap<>();
addRewrite(1005, 1010); //Play music disc
addRewrite(1003, 1005); //Iron door open
addRewrite(1006, 1011); //Iron door close
addRewrite(1004, 1009); //Fizz / Fire extinguished
addRewrite(1007, 1015); //Ghast charge / warns
addRewrite(1008, 1016); //Ghast shoot
addRewrite(1009, 1016); //Ghast shoot (Lower volume according to wiki.vg)
addRewrite(1010, 1019); //Zombie attacks wood door
addRewrite(1011, 1020); //Zombie attacks metal door
addRewrite(1012, 1021); //Zombie breaks wood door
addRewrite(1014, 1024); //Wither shoot
addRewrite(1015, 1025); //Bat takeoff / aka herobrine
addRewrite(1016, 1026); //Zombie inject
addRewrite(1017, 1027); //Zombie villager converted
addRewrite(1020, 1029); //Anvil break
addRewrite(1021, 1030); //Anvil use
addRewrite(1022, 1031); //Anvil land
}
public static int getNewId(int id) {
if (!contains(id))
return id;
return effects.get(id);
}
public static boolean contains(int oldId) {
return effects.containsKey(oldId);
}
private static void addRewrite(int oldId, int newId) {
effects.put(oldId, newId);
}
}
@@ -64,7 +64,7 @@ public enum SoundEffect {
MOB_HORSE_WOOD("mob.horse.wood", "entity.horse.step_wood", SoundCategory.NEUTRAL),
MOB_ZOMBIE_UNFECT("mob.zombie.unfect", "entity.zombie_villager.converted", SoundCategory.HOSTILE),
RANDOM_ANVIL_USE("random.anvil_use", "block.anvil.use", SoundCategory.BLOCK),
RANDOM_CHESTCLOSED("random.chestclosed", "block.enderchest.close", SoundCategory.BLOCK),
RANDOM_CHESTCLOSED("random.chestclosed", "block.chest.close", SoundCategory.BLOCK),
MOB_SHEEP_SHEAR("mob.sheep.shear", "entity.sheep.shear", SoundCategory.NEUTRAL),
RANDOM_POP("random.pop", "entity.item.pickup", SoundCategory.PLAYER),
MOB_BAT_DEATH("mob.bat.death", "entity.bat.death", SoundCategory.NEUTRAL),
@@ -85,7 +85,7 @@ public enum SoundEffect {
MOB_HORSE_SKELETON_DEATH("mob.horse.skeleton.death", "entity.skeleton_horse.death", SoundCategory.NEUTRAL),
AMBIENT_WEATHER_RAIN("ambient.weather.rain", "weather.rain.above", SoundCategory.WEATHER),
PORTAL_TRIGGER("portal.trigger", "block.portal.trigger", SoundCategory.BLOCK),
RANDOM_CHESTOPEN("random.chestopen", "block.enderchest.open", SoundCategory.BLOCK),
RANDOM_CHESTOPEN("random.chestopen", "block.chest.open", SoundCategory.BLOCK),
MOB_HORSE_LAND("mob.horse.land", "entity.horse.land", SoundCategory.NEUTRAL),
MOB_SILVERFISH_STEP("mob.silverfish.step", "entity.silverfish.step", SoundCategory.HOSTILE),
MOB_BAT_TAKEOFF("mob.bat.takeoff", "entity.bat.takeoff", SoundCategory.NEUTRAL),
@@ -260,9 +260,10 @@ public enum SoundEffect {
private final boolean breaksound;
private static HashMap<String, SoundEffect> effects;
static {
effects = new HashMap<String, SoundEffect>();
for(SoundEffect e : SoundEffect.values()) {
effects = new HashMap<>();
for (SoundEffect e : SoundEffect.values()) {
effects.put(e.getName(), e);
}
}
@@ -283,7 +284,7 @@ public enum SoundEffect {
public static SoundEffect getByName(String name) {
name = name.toLowerCase();
if(effects.containsKey(name))
if (effects.containsKey(name))
return effects.get(name);
return null;
}
@@ -4,10 +4,12 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.Getter;
import org.bukkit.Bukkit;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
@@ -17,13 +19,17 @@ public class ClientChunks extends StoredObject {
// Reflection
private static ReflectionUtil.ClassReflection mapChunkBulkRef;
private static ReflectionUtil.ClassReflection mapChunkRef;
private static Method obfuscateRef;
private static Class<?> worldRef;
static {
try {
mapChunkBulkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunkBulk"));
mapChunkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunk"));
obfuscateRef = Class.forName("org.spigotmc.AntiXray").getMethod("obfuscate", int.class, int.class, int.class, byte[].class, ReflectionUtil.nms("World"));
worldRef = ReflectionUtil.nms("World");
} catch (Exception e) {
Bukkit.getLogger().log(Level.WARNING, "Failed to initialise chunk reflection", e);
Bukkit.getLogger().log(Level.WARNING, "Failed to initialise chunks reflection", e);
}
}
@@ -44,6 +50,24 @@ public class ClientChunks extends StoredObject {
int[] xcoords = mapChunkBulkRef.getFieldValue("a", packet, int[].class);
int[] zcoords = mapChunkBulkRef.getFieldValue("b", packet, int[].class);
Object[] chunkMaps = mapChunkBulkRef.getFieldValue("c", packet, Object[].class);
if (ViaVersion.getConfig().isAntiXRay() && ViaVersion.getInstance().isSpigot()) { //Spigot anti-xray patch
try {
Object world = mapChunkBulkRef.getFieldValue("world", packet, Object.class);
for (int i = 0; i < xcoords.length; ++i) {
Object spigotConfig = ReflectionUtil.getPublic(world, "spigotConfig", Object.class);
Object antiXrayInstance = ReflectionUtil.getPublic(spigotConfig, "antiXrayInstance", Object.class);
Object b = ReflectionUtil.get(chunkMaps[i], "b", Object.class);
Object a = ReflectionUtil.get(chunkMaps[i], "a", Object.class);
obfuscateRef.invoke(antiXrayInstance, xcoords[i], zcoords[i], b, a, world);
}
} catch (Exception e) {
// not spigot, or it failed.
}
}
for (int i = 0; i < chunkMaps.length; i++) {
int x = xcoords[i];
int z = zcoords[i];
@@ -57,7 +81,7 @@ public class ClientChunks extends StoredObject {
list.add(chunkPacket);
}
} catch (Exception e) {
Bukkit.getLogger().log(Level.WARNING, "Failed to transform chunk bulk", e);
Bukkit.getLogger().log(Level.WARNING, "Failed to transform chunks bulk", e);
}
return list;
}
@@ -2,14 +2,12 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Sets;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.boss.BossBar;
@@ -22,28 +20,36 @@ import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chat.GameMode;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.MetadataRewriter;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.NewType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Getter
public class EntityTracker extends StoredObject {
private final Map<Integer, UUID> uuidMap = new HashMap<>();
private final Map<Integer, EntityType> clientEntityTypes = new HashMap<>();
private final Map<Integer, Integer> vehicleMap = new HashMap<>();
private final Map<Integer, BossBar> bossBarMap = new HashMap<>();
private final Set<Integer> validBlocking = new HashSet<>();
private final Set<Integer> knownHolograms = new HashSet<>();
private final Map<Integer, UUID> uuidMap = new ConcurrentHashMap<>();
private final Map<Integer, EntityType> clientEntityTypes = new ConcurrentHashMap<>();
private final Map<Integer, List<Metadata>> metadataBuffer = new ConcurrentHashMap<>();
private final Map<Integer, Integer> vehicleMap = new ConcurrentHashMap<>();
private final Map<Integer, BossBar> bossBarMap = new ConcurrentHashMap<>();
private final Set<Integer> validBlocking = Sets.newConcurrentHashSet();
private final Set<Integer> knownHolograms = Sets.newConcurrentHashSet();
private final Cache<Position, Material> blockInteractions = CacheBuilder.newBuilder().maximumSize(10).expireAfterAccess(250, TimeUnit.MILLISECONDS).build();
@Setter
private boolean blocking = false;
@Setter
private boolean autoTeam = false;
@Setter
private Long lastPlaceBlock = -1L;
@Setter
private int entityID;
@Setter
private Position currentlyDigging = null;
private boolean teamExists = false;
@Setter
private GameMode gameMode;
public EntityTracker(UserConnection user) {
super(user);
@@ -69,7 +75,7 @@ public class EntityTracker extends StoredObject {
wrapper.write(Type.VAR_INT, 1); // slot
wrapper.write(Type.ITEM, item);
try {
wrapper.send();
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
@@ -81,6 +87,7 @@ public class EntityTracker extends StoredObject {
uuidMap.remove(entityID);
validBlocking.remove(entityID);
knownHolograms.remove(entityID);
metadataBuffer.remove(entityID);
BossBar bar = bossBarMap.remove(entityID);
if (bar != null) {
@@ -107,7 +114,9 @@ public class EntityTracker extends StoredObject {
}
public void handleMetadata(int entityID, List<Metadata> metadataList) {
if (!clientEntityTypes.containsKey(entityID)) return;
if (!clientEntityTypes.containsKey(entityID)) {
return;
}
EntityType type = clientEntityTypes.get(entityID);
for (Metadata metadata : new ArrayList<>(metadataList)) {
@@ -126,11 +135,24 @@ public class EntityTracker extends StoredObject {
}
}
if (type == EntityType.SKELETON) {
if ((getMetaByIndex(metadataList, 12)) == null) {
metadataList.add(new Metadata(12, NewType.Boolean.getTypeID(), Type.BOOLEAN, true));
}
}
//ECHOPET Patch
if (type == EntityType.HORSE) {
// Wrong metadata value from EchoPet, patch since it's discontinued. (https://github.com/DSH105/EchoPet/blob/06947a8b08ce40be9a518c2982af494b3b99d140/modules/API/src/main/java/com/dsh105/echopet/compat/api/entity/HorseArmour.java#L22)
if (metadata.getId() == 16 && (int) metadata.getValue() == Integer.MIN_VALUE)
metadata.setValue(0);
}
if (type == EntityType.PLAYER) {
if (metadata.getId() == 0) {
// Byte
byte data = (byte) metadata.getValue();
if (entityID != getEntityID() && ((ViaVersionPlugin) ViaVersion.getInstance()).isShieldBlocking()) {
if (entityID != getEntityID() && ViaVersion.getConfig().isShieldBlocking()) {
if ((data & 0x10) == 0x10) {
if (validBlocking.contains(entityID)) {
Item shield = new Item((short) 442, (byte) 1, (short) 0, null);
@@ -142,11 +164,13 @@ public class EntityTracker extends StoredObject {
}
}
}
if (type == EntityType.ARMOR_STAND && ((ViaVersionPlugin) ViaVersion.getInstance()).isHologramPatch()) {
if (type == EntityType.ARMOR_STAND && ViaVersion.getConfig().isHologramPatch()) {
if (metadata.getId() == 0 && getMetaByIndex(metadataList, 10) != null) {
Metadata meta = getMetaByIndex(metadataList, 10); //Only happens if the armorstand is small
byte data = (byte) metadata.getValue();
if ((data & 0x20) == 0x20 && ((byte) meta.getValue() & 0x01) == 0x01) {
// Check invisible | Check small | Check if custom name is empty | Check if custom name visible is true
if ((data & 0x20) == 0x20 && ((byte) meta.getValue() & 0x01) == 0x01
&& ((String) getMetaByIndex(metadataList, 2).getValue()).length() != 0 && (boolean) getMetaByIndex(metadataList, 3).getValue()) {
if (!knownHolograms.contains(entityID)) {
knownHolograms.add(entityID);
try {
@@ -155,7 +179,7 @@ public class EntityTracker extends StoredObject {
Type.VAR_INT.write(buf, 0x25); // Relative Move Packet
Type.VAR_INT.write(buf, entityID);
buf.writeShort(0);
buf.writeShort((short) (128D * (((ViaVersionPlugin) ViaVersion.getInstance()).getHologramYOffset() * 32D)));
buf.writeShort((short) (128D * (ViaVersion.getConfig().getHologramYOffset() * 32D)));
buf.writeShort(0);
buf.writeBoolean(true);
getUser().sendRawPacket(buf, false);
@@ -165,9 +189,9 @@ public class EntityTracker extends StoredObject {
}
}
}
Player player = Bukkit.getPlayer(getUser().get(ProtocolInfo.class).getUuid());
UUID uuid = getUser().get(ProtocolInfo.class).getUuid();
// Boss bar
if (((ViaVersionPlugin) ViaVersion.getInstance()).isBossbarPatch()) {
if (ViaVersion.getConfig().isBossbarPatch()) {
if (type == EntityType.ENDER_DRAGON || type == EntityType.WITHER) {
if (metadata.getId() == 2) {
BossBar bar = bossBarMap.get(entityID);
@@ -176,12 +200,12 @@ public class EntityTracker extends StoredObject {
if (bar == null) {
bar = ViaVersion.getInstance().createBossBar(title, BossColor.PINK, BossStyle.SOLID);
bossBarMap.put(entityID, bar);
bar.addPlayer(player);
bar.addPlayer(uuid);
bar.show();
} else {
bar.setTitle(title);
}
} else if (metadata.getId() == 6 && !((ViaVersionPlugin) ViaVersion.getInstance()).isBossbarAntiflicker()) { // If anti flicker is enabled, don't update health
} else if (metadata.getId() == 6 && !ViaVersion.getConfig().isBossbarAntiflicker()) { // If anti flicker is enabled, don't update health
BossBar bar = bossBarMap.get(entityID);
// Make health range between 0 and 1
float maxHealth = type == EntityType.ENDER_DRAGON ? 200.0f : 300.0f;
@@ -190,7 +214,7 @@ public class EntityTracker extends StoredObject {
String title = type == EntityType.ENDER_DRAGON ? "Ender Dragon" : "Wither";
bar = ViaVersion.getInstance().createBossBar(title, health, BossColor.PINK, BossStyle.SOLID);
bossBarMap.put(entityID, bar);
bar.addPlayer(player);
bar.addPlayer(uuid);
bar.show();
} else {
bar.setHealth(health);
@@ -232,9 +256,35 @@ public class EntityTracker extends StoredObject {
}
teamExists = b;
try {
wrapper.send();
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addMetadataToBuffer(int entityID, List<Metadata> metadataList) {
if (metadataBuffer.containsKey(entityID)) {
metadataBuffer.get(entityID).addAll(metadataList);
} else {
metadataBuffer.put(entityID, metadataList);
}
}
public void sendMetadataBuffer(int entityID) {
if (metadataBuffer.containsKey(entityID)) {
PacketWrapper wrapper = new PacketWrapper(0x39, null, getUser());
wrapper.write(Type.VAR_INT, entityID);
wrapper.write(Protocol1_9TO1_8.METADATA_LIST, metadataBuffer.get(entityID));
MetadataRewriter.transform(getClientEntityTypes().get(entityID), metadataBuffer.get(entityID));
handleMetadata(entityID, metadataBuffer.get(entityID));
if (metadataBuffer.get(entityID).size() > 0) {
try {
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
}
metadataBuffer.remove(entityID);
}
}
}
@@ -1,6 +1,7 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage;
import lombok.Getter;
import lombok.Setter;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
@@ -9,6 +10,9 @@ public class MovementTracker extends StoredObject {
private static final long IDLE_PACKET_LIMIT = 20; // Max 20 ticks behind
@Getter
private long nextIdlePacket = 0L;
@Getter
@Setter
private boolean ground = true;
public MovementTracker(UserConnection user) {
super(user);
@@ -0,0 +1,26 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage;
import lombok.Getter;
import lombok.Setter;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
@Getter
public class PlaceBlockTracker extends StoredObject {
private long lastPlaceTimestamp = 0;
@Setter
private Position lastPlacedPosition = null;
public PlaceBlockTracker(UserConnection user) {
super(user);
}
public boolean isExpired(int ms) {
return System.currentTimeMillis() > (lastPlaceTimestamp + ms);
}
public void updateTime() {
lastPlaceTimestamp = System.currentTimeMillis();
}
}
@@ -4,9 +4,11 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.bukkit.Bukkit;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
import us.myles.ViaVersion.api.type.PartialType;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.ChunkSection1_9to1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
import java.nio.ByteBuffer;
@@ -17,11 +19,11 @@ import java.util.logging.Level;
public class ChunkType extends PartialType<Chunk, ClientChunks> {
/**
* Amount of sections in a chunk.
* Amount of sections in a chunks.
*/
private static final int SECTION_COUNT = 16;
public static final int SECTION_COUNT = 16;
/**
* size of each chunk section (16x16x16).
* size of each chunks section (16x16x16).
*/
private static final int SECTION_SIZE = 16;
/**
@@ -37,6 +39,11 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
return ((long) msw << 32) + lsw - -2147483648L;
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
@Override
public Chunk read(ByteBuf input, ClientChunks param) throws Exception {
int chunkX = input.readInt();
@@ -48,7 +55,7 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
// Data to be read
BitSet usedSections = new BitSet(16);
ChunkSection[] sections = new ChunkSection[16];
ChunkSection1_9to1_8[] sections = new ChunkSection1_9to1_8[16];
byte[] biomeData = null;
// Calculate section count from bitmask
@@ -59,33 +66,34 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
}
int sectionCount = usedSections.cardinality(); // the amount of sections set
// If the chunk is from a chunk bulk, it is never an unload packet
// If the chunks is from a chunks bulk, it is never an unload packet
// Other wise, if it has no data, it is :)
boolean isBulkPacket = param.getBulkChunks().remove(chunkHash);
if (sectionCount == 0 && groundUp && !isBulkPacket && param.getLoadedChunks().contains(chunkHash)) {
// This is a chunk unload packet
// This is a chunks unload packet
param.getLoadedChunks().remove(chunkHash);
return new Chunk(chunkX, chunkZ);
return new Chunk1_9to1_8(chunkX, chunkZ);
}
int startIndex = input.readerIndex();
param.getLoadedChunks().add(chunkHash); // mark chunk as loaded
param.getLoadedChunks().add(chunkHash); // mark chunks as loaded
// Read blocks
for (int i = 0; i < SECTION_COUNT; i++) {
if (!usedSections.get(i)) continue; // Section not set
ChunkSection section = new ChunkSection();
ChunkSection1_9to1_8 section = new ChunkSection1_9to1_8();
sections[i] = section;
// Read block data and convert to short buffer
byte[] blockData = new byte[ChunkSection.SIZE * 2];
byte[] blockData = new byte[ChunkSection1_9to1_8.SIZE * 2];
input.readBytes(blockData);
ShortBuffer blockBuf = ByteBuffer.wrap(blockData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
for (int j = 0; j < ChunkSection.SIZE; j++) {
for (int j = 0; j < ChunkSection1_9to1_8.SIZE; j++) {
int mask = blockBuf.get();
int type = mask >> 4;
int data = mask & 0xF;
section.setBlock(j, type, data);
}
}
@@ -93,20 +101,20 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
// 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];
byte[] blockLightArray = new byte[ChunkSection1_9to1_8.LIGHT_LENGTH];
input.readBytes(blockLightArray);
sections[i].setBlockLight(blockLightArray);
}
// Read sky light
int bytesLeft = dataLength - (input.readerIndex() - startIndex);
if (bytesLeft >= ChunkSection.LIGHT_LENGTH) {
if (bytesLeft >= ChunkSection1_9to1_8.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];
byte[] skyLightArray = new byte[ChunkSection1_9to1_8.LIGHT_LENGTH];
input.readBytes(skyLightArray);
sections[i].setSkyLight(skyLightArray);
bytesLeft -= ChunkSection.LIGHT_LENGTH;
bytesLeft -= ChunkSection1_9to1_8.LIGHT_LENGTH;
}
}
@@ -119,20 +127,18 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
// Check remaining bytes
if (bytesLeft > 0) {
Bukkit.getLogger().log(Level.WARNING, bytesLeft + " Bytes left after reading chunk! (" + groundUp + ")");
Bukkit.getLogger().log(Level.WARNING, bytesLeft + " Bytes left after reading chunks! (" + groundUp + ")");
}
// Return chunk
return new Chunk(chunkX, chunkZ, groundUp, bitmask, sections, biomeData);
// Return chunks
return new Chunk1_9to1_8(chunkX, chunkZ, groundUp, bitmask, sections, biomeData);
}
@Override
public void write(ByteBuf output, ClientChunks param, Chunk chunk) throws Exception {
if (chunk.isUnloadPacket()) {
output.clear();
Type.VAR_INT.write(output, 0x1D); // Unload packet ID
}
public void write(ByteBuf output, ClientChunks param, Chunk input) throws Exception {
if (!(input instanceof Chunk1_9to1_8)) throw new Exception("Incompatible chunk, " + input.getClass());
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) input;
// Write primary info
output.writeInt(chunk.getX());
output.writeInt(chunk.getZ());
@@ -142,12 +148,14 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
ByteBuf buf = Unpooled.buffer();
for (int i = 0; i < SECTION_COUNT; i++) {
ChunkSection section = chunk.getSections()[i];
ChunkSection1_9to1_8 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);
Type.VAR_INT.write(output, buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0));
@@ -158,5 +166,9 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
if (chunk.hasBiomeData()) {
output.writeBytes(chunk.getBiomeData());
}
}
}
@@ -2,16 +2,13 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.minecraft.MetaListTypeTemplate;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import java.util.ArrayList;
import java.util.List;
public class MetadataListType extends Type<List<Metadata>> {
public MetadataListType() {
super("Metadata List", List.class);
}
public class MetadataListType extends MetaListTypeTemplate {
@Override
public List<Metadata> read(ByteBuf buffer) throws Exception {
@@ -3,14 +3,10 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.minecraft.MetaTypeTemplate;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.MetadataTypes;
public class MetadataType extends Type<Metadata> {
public MetadataType() {
super(Metadata.class);
}
public class MetadataType extends MetaTypeTemplate {
@Override
public Metadata read(ByteBuf buffer) throws Exception {
@@ -0,0 +1,55 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_9_1;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
public class Protocol1_9TO1_9_1 extends Protocol {
@Override
protected void registerPackets() {
// Currently supports 1.9.1 and 1.9.2
// Join Game Packet
registerOutgoing(State.PLAY, 0x23, 0x23, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Player ID
map(Type.UNSIGNED_BYTE); // 1 - Player Gamemode
// 1.9.1 PRE 2 Changed this
map(Type.INT, Type.BYTE); // 2 - Player Dimension
map(Type.UNSIGNED_BYTE); // 3 - World Difficulty
map(Type.UNSIGNED_BYTE); // 4 - Max Players (Tab)
map(Type.STRING); // 5 - Level Type
map(Type.BOOLEAN); // 6 - Reduced Debug info
}
});
// Sound Effect Packet
registerOutgoing(State.PLAY, 0x47, 0x47, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Sound ID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int sound = wrapper.get(Type.VAR_INT, 0);
if (sound == 415) // Stop the Elytra sound for 1.9 (It's introduced in 1.9.2)
wrapper.cancel();
else if (sound >= 416) // Act like the Elytra sound never existed
wrapper.set(Type.VAR_INT, 0, sound - 1);
}
});
}
});
}
@Override
public void init(UserConnection userConnection) {
}
}
@@ -5,6 +5,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
@RequiredArgsConstructor
public class UpdateListener implements Listener {
@@ -14,7 +15,7 @@ public class UpdateListener implements Listener {
@EventHandler
public void onJoin(PlayerJoinEvent e) {
if (e.getPlayer().hasPermission("viaversion.update")
&& plugin.isCheckForUpdates()) {
&& ViaVersion.getConfig().isCheckForUpdates()) {
UpdateUtil.sendUpdateMessage(e.getPlayer().getUniqueId(), plugin);
}
}
@@ -1,7 +1,6 @@
package us.myles.ViaVersion.util;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.ByteToMessageDecoder;
@@ -0,0 +1,33 @@
package us.myles.ViaVersion.util;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ProtocolSupportUtil {
private static Method protocolVersionMethod = null;
private static Method getIdMethod = null;
static {
try {
protocolVersionMethod = Class.forName("protocolsupport.api.ProtocolSupportAPI").getMethod("getProtocolVersion", Player.class);
getIdMethod = Class.forName("protocolsupport.api.ProtocolVersion").getMethod("getId");
} catch (Exception e) {
// ProtocolSupport not installed.
}
}
public static int getProtocolVersion(Player player) {
if (protocolVersionMethod == null) return -1;
try {
Object version = protocolVersionMethod.invoke(null, player);
return (int) getIdMethod.invoke(version);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
}
@@ -22,6 +22,10 @@ public class ReflectionUtil {
return Class.forName(BASE + "." + className);
}
public static String getVersion() {
return BASE.substring(BASE.lastIndexOf('.') + 1);
}
public static Object invokeStatic(Class<?> clazz, String method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method m = clazz.getDeclaredMethod(method);
return m.invoke(null);
@@ -56,6 +60,13 @@ public class ReflectionUtil {
return (T) field.get(o);
}
public static <T> T getPublic(Object o, String f, Class<T> t) throws NoSuchFieldException, IllegalAccessException {
Field field = o.getClass().getField(f);
field.setAccessible(true);
return (T) field.get(o);
}
public static void set(Object o, String f, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = o.getClass().getDeclaredField(f);
field.setAccessible(true);
+4
View File
@@ -0,0 +1,4 @@
name: ViaVersion
main: us.myles.ViaVersion.Bungee
author: _MylesC
version: ${project.version}
+53 -7
View File
@@ -1,5 +1,46 @@
# Thanks for downloading ViaVersion
# Ensure you look through all these options
# If you need help:
# viaversion.com - Discussion tab
# IRC - https://elmer.spi.gt/iris/?nick=&channels=viaversion #viaversion on irc.spi.gt
#
#----------------------------------------------------------#
# GLOBAL OPTIONS #
#----------------------------------------------------------#
#
# Should ViaVersion check for updates?
checkforupdates: true
# Send the supported versions with the Status (Ping) response packet
send-supported-versions: false
#
#----------------------------------------------------------#
# GLOBAL PACKET LIMITER #
#----------------------------------------------------------#
#
# Anti-Cheat, Packets Per Second (PPS) limiter
# Clients by default send around 20-90 packets per second.
# What is the maximum per second a client can send (Use %pps to display their pps)
# Use -1 to disable.
max-pps: 600
max-pps-kick-msg: "You are sending too many packets!"
# We can also kick them if over a period they send over a threshold a certain amount of times.
# Period to track (in seconds)
# Use -1 to disable.
tracking-period: 6
# How many packets per second counts as a warning
tracking-warning-pps: 120
# How many warnings over the interval can we have
# This can never be higher than "tracking-interval"
tracking-max-warnings: 4
tracking-max-kick-msg: "You are sending too many packets, :("
#
#----------------------------------------------------------#
# 1.9 & 1.10 CLIENTS ON 1.8 SERVERS OPTIONS #
#----------------------------------------------------------#
#
# No collide options, these allow you to configure how collision works.
# Do you want us to prevent collision?
prevent-collision: true
@@ -7,21 +48,26 @@ prevent-collision: true
auto-team: true
# When enabled if certain metadata can't be read we won't tell you about it
suppress-metadata-errors: false
# When enabled 1.9 will be able to block by using shields
# When enabled 1.9 & 1.10 will be able to block by using shields
shield-blocking: true
# Should we enable our hologram patch?
# If they're in the wrong place enable this
hologram-patch: false
# This is the offset, should work as default when enabled.
hologram-y: -1
hologram-y: -0.96
# Enable player tick simulation, this fixes eating, drinking, nether portals.
simulate-pt: true
# Should we use nms player to simulate packets, (may fix anti-cheat issues)
nms-player-ticking: true
# Should we patch boss bars so they work? (Default: true, disable if you're having issues)
bossbar-patch: true
# If your boss bar flickers on 1.9, set this to 'true'. It will keep all boss bars on 100% (not recommended)
# If your boss bar flickers on 1.9 & 1.10, set this to 'true'. It will keep all boss bars on 100% (not recommended)
bossbar-anti-flicker: false
# This will show the new effect indicator in the top-right corner for 1.9 players.
# This will show the new effect indicator in the top-right corner for 1.9 & 1.10 players.
use-new-effect-indicator: true
# This will suppress the following error: 'Unable to get entity for ID: xxxx'
# This error message means one of you plugins is sending bad packets!
suppress-entityid-errors: false
# Show the new death messages for 1.9 & 1.10 on the death screen
use-new-deathmessages: false
# Should we cache our items, this will prevent server from being lagged out, however the cost is a constant task caching items
item-cache: true
# Patch the Anti xray to work on 1.9 & 1.10 (If your server is 1.8) This can cost more performance, so disable it if you don't use it.
anti-xray-patch: true
+3 -2
View File
@@ -2,8 +2,9 @@ name: ViaVersion
main: us.myles.ViaVersion.ViaVersionPlugin
author: _MylesC
version: ${project.version}
load: startup
loadbefore: [ProtocolLib, ProxyPipe, SpigotLib, PacketListenerApi]
load: postworld
loadbefore: [ProtocolLib, ProxyPipe, SpigotLib, PacketListenerApi, SkinRestorer]
softdepend: [ProtocolSupport]
commands:
viaversion:
description: Shows ViaVersion Version and more.