Compare commits

..
119 Commits
Author SHA1 Message Date
Myles 7a9cb86563 0.9.7 release 2016-07-08 12:55:05 +01:00
Myles 87b1919863 Let the normal send go through the event loop. Fix signs are sent before the chunks on a 1.9.4 server with a 1.9-1.9.2 client (#460) 2016-07-03 12:05:32 +01:00
Myles d164e413c2 Snapshot 2016-07-02 23:34:31 +01:00
Myles 76d7db044f Version bump + small dead code fix 2016-07-02 23:28:54 +01:00
Myles 046ebb8ecc Fix chunk bug 2016-07-02 23:22:44 +01:00
Myles bccde04ace Back to snapshot 2016-07-02 22:37:29 +01:00
Myles 9385f03722 Bump version and tiny bit of formatting fix 2016-07-02 22:33:54 +01:00
Myles b48fc17ead Transform block 36 for 1.9.2 reading, and also use PartialType. 2016-07-02 22:32:40 +01:00
Myles b50d0075ca PacketWrapper allow returning of ChannelFuture with send, and fix blocked protocols disconnect.
Also remove a TODO.
2016-07-02 22:19:43 +01:00
Myles 84d006c71d Merge branch 'lenis-patch' 2016-07-02 22:06:39 +01:00
Myles 212da9c46c Fix resetReader being wrong, ty @Matsv for helping solve this 2016-07-02 22:05:08 +01:00
Myles 8edb7048a1 Code cleanup and implement index fix by @Matsv 2016-07-02 21:23:28 +01:00
Myles abe3c4b0c9 Identify unsigned byte properly 2016-07-02 21:07:41 +01:00
Myles c6d3c97c65 Fix clearing of wrapper 2016-07-02 21:07:22 +01:00
Matsv 13c6a9d612 Change getBlockId to get instead of indexOf 2016-07-02 20:45:34 +02:00
Myles 7d2ef743ac Fix stupidity, thanks @Pokechu22 <3 2016-07-02 19:09:38 +01:00
Myles 598d0f96d8 Not sure if i'm working forward or backwards on this, but pushing for progress 2016-07-01 20:17:25 +01:00
Matsv cbc22922d3 Set default force-sign-transformer to false 2016-07-01 18:51:42 +02:00
Myles 49783d36c2 Add link to docs. 2016-07-01 17:13:01 +01:00
Myles 70d91b4f87 Blocked versions now included when server sends supported versions, javadoc fixes & simple colour for blocked-msg. 2016-07-01 16:36:19 +01:00
Myles 17f3d13267 Fix #450, ProtocolSupport concurrency issues, also make listwrapper sync'd 2016-06-30 00:26:12 +01:00
Myles 559674bf58 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-06-29 16:43:42 +01:00
Myles fbc298bc30 Add some more detail to the config 2016-06-29 16:38:38 +01:00
Matsv e36902c441 Add possibility to block specific protocol versions 2016-06-29 12:26:03 +02:00
Matsv 805c78989a Port all the clients to prevent issues like ViaVersions BossBarAPI not working for clients on the same protocol. (If you use the api on a 1.9> server) 2016-06-29 11:35:15 +02:00
Mats 85e416171c Let the transformer handle the signs (#456) 2016-06-28 13:16:36 +02:00
Mats b55c0d0783 Possibility to force the json transformer (#455)
* Possibility to force the json transform

* Escape all the Strings. Who likes double quotes anyways.
2016-06-26 21:47:08 +01:00
Myles 33b4b3835f Update Contributors and version in README 2016-06-25 23:38:04 +01:00
Matsv 8ccd772105 Change the formatting a bit. merged #421 2016-06-25 13:22:18 +02:00
Matsv 8677c632df Merge remote-tracking branch 'jor/potion' into upmaster 2016-06-25 13:19:14 +02:00
Myles 560476c56f Should fix #452 2016-06-24 14:02:29 +01:00
lenis0012 20bbe7cec6 Support 1.9.2 servers
This code basically reads 1.9 data so it can then be transformed to add
in the tile entities.
it geta a little funky when it already loaded it as a 1.8 chunk tho, so
i had to add some methods to the chunk api.

in short: I hate minecraft
2016-06-23 04:00:59 +02:00
Myles 1a002486d0 Move back to snapshot, oops 2016-06-22 23:37:58 +01:00
Myles 26fcdfe3bc Allow ability to replace piston extension block to prevent crashing. 2016-06-22 23:34:32 +01:00
Mats 4af98b220c Fix piston tile entity crash (#443) 2016-06-22 21:37:53 +01:00
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
joserobjr a79533bf72 Fixed uncraftable potion conversions from 1.8 to 1.9 2016-06-08 10:31:03 -03:00
joserobjr e3510d01d6 Fixes potion data conversion from 1.8 to 1.9 2016-06-08 08:09:13 -03: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
82 changed files with 3108 additions and 853 deletions

No files matched your search

+17 -13
View File
@@ -1,33 +1,34 @@
# ViaVersion 0.8.2
# ViaVersion 0.9.7
[![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**
**Allows the connection of higher client versions to lower server versions**
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
Allows 1.10, 1.9 on 1.8 server and 1.10 on a 1.9 server.
###*Don't use late bind*
This took 100s hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
**User Docs:** https://docs.viaversion.com/display/VIAVERSION/
**Contributor Chat:** https://gitter.im/MylesIsCool/ViaVersion
Sources:
--------
**wiki.vg** (Protocol info, though I did have to find a lot myself)
**Burger** We use this tool to get protocol and other useful information for new / undocumented Minecraft versions
**OpenNBT** (used for slot rewriting)
Contributors:
All Time Contributors:
--------
**Myself** (harhar)
**Matsv/StamBoom**
**Matsv**
**HugoDaBosss**
@@ -41,6 +42,10 @@ Contributors:
**lenis0012**
**fuzzy_bot**
**joserobjr**
Releases / Dev Builds:
@@ -52,14 +57,13 @@ https://www.spigotmc.org/resources/viaversion.19254/history
----------
You can find unofficial dev builds here:
You can find official dev builds here:
**Terrobility's Build Server:** https://ci.terrocidepvp.net/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:
--------
+16 -1
View File
@@ -6,7 +6,7 @@
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>0.8.3-SNAPSHOT</version>
<version>0.9.7</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>
@@ -135,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>
@@ -204,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,183 @@
package us.myles.ViaVersion;
import us.myles.ViaVersion.api.ViaVersionConfig;
import us.myles.ViaVersion.util.Configuration;
import java.io.File;
import java.util.List;
public class ViaConfig implements ViaVersionConfig {
private final ViaVersionPlugin plugin;
public ViaConfig(ViaVersionPlugin plugin) {
this.plugin = plugin;
generateConfig();
}
public void generateConfig() {
File file = new File(plugin.getDataFolder(), "config.yml");
if (file.exists()) {
// Update conf options
Configuration oldConfig = new Configuration(file);
oldConfig.reload(false); // Load current options from conf
file.delete(); // Delete old conf
plugin.saveDefaultConfig(); // Generate new conf
Configuration newConfig = new Configuration(file);
newConfig.reload(true); // Load default options
for (String key : oldConfig.getKeys(false)) {
// Set option in new conf if exists
if (newConfig.contains(key)) {
newConfig.set(key, oldConfig.get(key));
}
}
newConfig.save();
} else {
plugin.saveDefaultConfig();
}
}
@Override
public boolean isCheckForUpdates() {
return plugin.getConfig().getBoolean("checkforupdates", true);
}
@Override
public boolean isPreventCollision() {
return plugin.getConfig().getBoolean("prevent-collision", true);
}
@Override
public boolean isNewEffectIndicator() {
return plugin.getConfig().getBoolean("use-new-effect-indicator", true);
}
@Override
public boolean isShowNewDeathMessages() {
return plugin.getConfig().getBoolean("use-new-deathmessages", false);
}
@Override
public boolean isSuppressMetadataErrors() {
return plugin.getConfig().getBoolean("suppress-metadata-errors", false);
}
@Override
public boolean isShieldBlocking() {
return plugin.getConfig().getBoolean("shield-blocking", true);
}
@Override
public boolean isHologramPatch() {
return plugin.getConfig().getBoolean("hologram-patch", false);
}
@Override
public boolean isBossbarPatch() {
return plugin.getConfig().getBoolean("bossbar-patch", true);
}
@Override
public boolean isBossbarAntiflicker() {
return plugin.getConfig().getBoolean("bossbar-anti-flicker", false);
}
@Override
public boolean isUnknownEntitiesSuppressed() {
return false;
}
@Override
public double getHologramYOffset() {
return plugin.getConfig().getDouble("hologram-y", -0.96D);
}
@Override
public boolean isBlockBreakPatch() {
return false;
}
@Override
public int getMaxPPS() {
return plugin.getConfig().getInt("max-pps", 140);
}
@Override
public String getMaxPPSKickMessage() {
return plugin.getConfig().getString("max-pps-kick-msg", "Sending packets too fast? lag?");
}
@Override
public int getTrackingPeriod() {
return plugin.getConfig().getInt("tracking-period", 6);
}
@Override
public int getWarningPPS() {
return plugin.getConfig().getInt("tracking-warning-pps", 120);
}
@Override
public int getMaxWarnings() {
return plugin.getConfig().getInt("tracking-max-warnings", 3);
}
@Override
public String getMaxWarningsKickMessage() {
return plugin.getConfig().getString("tracking-max-kick-msg", "You are sending too many packets, :(");
}
@Override
public boolean isAntiXRay() {
return plugin.getConfig().getBoolean("anti-xray-patch", true);
}
@Override
public boolean isSendSupportedVersions() {
return plugin.getConfig().getBoolean("send-supported-versions", false);
}
@Override
public boolean isStimulatePlayerTick() {
return plugin.getConfig().getBoolean("simulate-pt", true);
}
@Override
public boolean isItemCache() {
return plugin.getConfig().getBoolean("item-cache", true);
}
@Override
public boolean isNMSPlayerTicking() {
return plugin.getConfig().getBoolean("nms-player-ticking", true);
}
@Override
public boolean isReplacePistons() {
return plugin.getConfig().getBoolean("replace-pistons", false);
}
@Override
public int getPistonReplacementId() {
return plugin.getConfig().getInt("replacement-piston-id", 0);
}
public boolean isAutoTeam() {
// Collision has to be enabled first
return isPreventCollision() && plugin.getConfig().getBoolean("auto-team", true);
}
@Override
public boolean isForceJsonTransform() {
return plugin.getConfig().getBoolean("force-json-transform", false);
}
@Override
public List<Integer> getBlockedProtocols() {
return plugin.getConfig().getIntegerList("block-protocols");
}
@Override
public String getBlockedDisconnectMsg() {
return plugin.getConfig().getString("block-disconnect-msg", "You are using an unsupported Minecraft version!");
}
}
@@ -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;
@@ -26,23 +27,20 @@ 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.ConcurrentList;
import us.myles.ViaVersion.util.ListWrapper;
import us.myles.ViaVersion.util.ProtocolSupportUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.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<>();
@@ -50,11 +48,33 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
private ViaCommandHandler commandHandler;
private boolean debug = false;
private boolean compatSpigotBuild = false;
private boolean spigot = true;
private boolean lateBind = false;
private boolean protocolSupport = false;
@Getter
private ViaConfig conf;
public ViaVersionPlugin() {
// Check if we're using protocol support too
protocolSupport = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
if (protocolSupport) {
getLogger().info("Patching to prevent concurrency issues...");
try {
patchLists();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@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.");
@@ -67,22 +87,35 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
}
}
// 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){
} catch (Exception e) {
compatSpigotBuild = false;
}
// Generate classes needed (only works if it's compat)
ClassGenerator.generate();
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded, injecting.");
injectPacketHandler();
// 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() {
@@ -96,6 +129,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
getLogger().warning("ViaVersion will not function on the current protocol.");
}
}
ProtocolRegistry.refreshVersions();
}
});
@@ -107,6 +141,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
@@ -162,42 +201,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;
@@ -238,6 +260,54 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
}
}
public void patchLists() throws Exception {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
return;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
if (!(value instanceof ConcurrentList)) {
ConcurrentList list = new ConcurrentList();
list.addAll((List) value);
field.set(connection, list);
}
}
}
}
public boolean isBinded() {
try {
Object connection = getServerConnection();
if (connection == null) {
return false;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
synchronized (value) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
return true;
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
}
return false;
}
private void inject(ChannelFuture future) {
try {
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
@@ -248,11 +318,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();
}
}
@@ -294,17 +371,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);
@@ -363,93 +448,17 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI, ViaVe
return compatSpigotBuild;
}
public boolean isCheckForUpdates() {
return getConfig().getBoolean("checkforupdates", true);
}
@Override
public SortedSet<Integer> getSupportedVersions() {
SortedSet<Integer> outputSet = new TreeSet<>(ProtocolRegistry.getSupportedVersions());
outputSet.removeAll(getConf().getBlockedProtocols());
public boolean isPreventCollision() {
return getConfig().getBoolean("prevent-collision", true);
}
public boolean isNewEffectIndicator() {
return getConfig().getBoolean("use-new-effect-indicator", true);
return outputSet;
}
@Override
public boolean isShowNewDeathMessages() {
return getConfig().getBoolean("use-new-deathmessages", false);
}
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 isBlockBreakPatch() {
return getConfig().getBoolean("block-break-patch", true);
}
@Override
public int getMaxPPS() {
return getConfig().getInt("max-pps", 140);
}
@Override
public String getMaxPPSKickMessage() {
return getConfig().getString("max-pps-kick-msg", "Sending packets too fast? lag?");
}
@Override
public int getTrackingPeriod() {
return getConfig().getInt("tracking-period", 6);
}
@Override
public int getWarningPPS() {
return getConfig().getInt("tracking-warning-pps", 120);
}
@Override
public int getMaxWarnings() {
return getConfig().getInt("tracking-max-warnings", 3);
}
@Override
public String getMaxWarningsKickMessage() {
return getConfig().getString("tracking-max-kick-msg", "You are sending too many packets, :(");
}
@Override
public boolean isAntiXRay() {
return getConfig().getBoolean("anti-xray-patch", true);
}
public boolean isAutoTeam() {
// Collision has to be enabled first
return isPreventCollision() && getConfig().getBoolean("auto-team", true);
public boolean isSpigot() {
return this.spigot;
}
public void addPortedClient(UserConnection info) {
@@ -479,33 +488,37 @@ 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 (getMaxPPS() > 0) {
if (info.getPacketsPerSecond() >= getMaxPPS()) {
info.disconnect(getMaxPPSKickMessage());
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 (getMaxWarnings() > 0 && getTrackingPeriod() > 0) {
if (info.getSecondsObserved() > getTrackingPeriod()) {
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() >= getWarningPPS()) {
if (info.getPacketsPerSecond() >= conf.getWarningPPS()) {
info.setWarnings(info.getWarnings() + 1);
}
if (info.getWarnings() >= getMaxWarnings()) {
info.disconnect(getMaxWarningsKickMessage());
if (info.getWarnings() >= conf.getMaxWarnings()) {
info.disconnect(conf.getMaxWarningsKickMessage().replace("%pps", ((Long) info.getPacketsPerSecond()).intValue() + ""));
return true; // don't send current packet
}
}
@@ -3,18 +3,24 @@ package us.myles.ViaVersion.api;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import lombok.Getter;
import lombok.Setter;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.ValueCreator;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
import us.myles.ViaVersion.exception.InformativeException;
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -38,8 +44,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;
@@ -76,12 +84,35 @@ public class PacketWrapper {
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;
@@ -102,6 +133,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
*/
@@ -117,7 +149,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) {
@@ -134,6 +166,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) {
@@ -154,6 +187,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
*/
@@ -173,7 +207,7 @@ public class PacketWrapper {
packetValues.addAll(readableObjects);
readableObjects.clear();
// If the buffer has readable bytes, copy them.
if(inputBuffer.readableBytes() > 0){
if (inputBuffer.readableBytes() > 0) {
passthrough(Type.REMAINING_BYTES);
}
}
@@ -190,6 +224,7 @@ public class PacketWrapper {
}
if (readableObjects.size() > 0) {
packetValues.addAll(readableObjects);
readableObjects.clear();
}
int index = 0;
@@ -234,7 +269,7 @@ public class PacketWrapper {
private void writeRemaining(ByteBuf output) {
if (inputBuffer != null) {
output.writeBytes(inputBuffer);
output.writeBytes(inputBuffer, inputBuffer.readableBytes());
}
}
@@ -243,10 +278,93 @@ public class PacketWrapper {
* Be careful not to send packets twice.
* (Sends it after current)
*
* @param packetProtocol - The protocol version of the packet.
* @param skipCurrentPipeline - Skip the current pipeline
* @throws Exception if it fails to write
*/
public void send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
if (!isCancelled()) {
ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline);
user().sendRawPacket(output);
}
}
/**
* Let the packet go through the protocol pipes and write it to ByteBuf
*
* @param packetProtocol - The protocol version of the packet.
* @param skipCurrentPipeline - Skip the current pipeline
* @return Packet buffer
* @throws Exception if it fails to write
*/
private ByteBuf constructPacket(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
// Apply current pipeline
List<Protocol> protocols = new ArrayList<>(user().get(ProtocolInfo.class).getPipeline().pipes());
// Other way if outgoing
Collections.reverse(protocols);
int index = 0;
for (int i = 0; i < protocols.size(); i++) {
if (protocols.get(i).getClass().equals(packetProtocol)) {
index = skipCurrentPipeline ? (i + 1) : (i);
break;
}
}
// Reset reader before we start
resetReader();
// Apply other protocols
apply(Direction.OUTGOING, user().get(ProtocolInfo.class).getState(), index, protocols);
// Send
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
writeToBuffer(output);
return output;
}
/**
* Send this packet to the associated user.
* Be careful not to send packets twice.
* (Sends it after current)
*
* @param packetProtocol - The protocol version of the packet.
* @throws Exception if it fails to write
*/
public void send(Class<? extends Protocol> packetProtocol) throws Exception {
send(packetProtocol, true);
}
/**
* Send this packet to the associated user.
* Be careful not to send packets twice.
* (Sends it after current)
* Also returns the packets ChannelFuture
*
* @param packetProtocol - The protocol version of the packet.
* @return The packets ChannelFuture
* @throws Exception if it fails to write
*/
public ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception {
if (!isCancelled()) {
ByteBuf output = constructPacket(packetProtocol, true);
return user().sendRawPacketFuture(output);
}
return user().getChannel().newFailedFuture(new Exception("Cancelled packet"));
}
/**
* Send this packet to the associated user.
* Be careful not to send packets twice.
* (Sends it after current)
* <br />
* <b>This method is no longer used, it's favoured to use send(Protocol) as it will handle the pipeline properly.</b>
*
* @throws Exception if it fails to write
*/
@Deprecated
public void send() throws Exception {
if (!isCancelled()) {
// Send
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
writeToBuffer(output);
user().sendRawPacket(output);
@@ -277,6 +395,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
*/
@@ -306,6 +444,10 @@ public class PacketWrapper {
* Reset the reader, so that it can be read again.
*/
public void resetReader() {
// Move readable objects are packet values
this.packetValues.addAll(readableObjects);
this.readableObjects.clear();
// Move all packet values to the readable for next packet.
this.readableObjects.addAll(packetValues);
this.packetValues.clear();
}
@@ -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();
}
}
@@ -6,7 +6,9 @@ import us.myles.ViaVersion.api.boss.BossBar;
import us.myles.ViaVersion.api.boss.BossColor;
import us.myles.ViaVersion.api.boss.BossStyle;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import java.util.SortedSet;
import java.util.UUID;
public interface ViaVersionAPI {
@@ -20,6 +22,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 +40,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);
@@ -109,4 +112,29 @@ public interface ViaVersionAPI {
* @return True if it is
*/
boolean isCompatSpigotBuild();
/**
* Get the supported protocol versions
* This method removes any blocked protocol versions.
* @see ProtocolRegistry#getSupportedVersions() for full list.
*
* @return a list of protocol versions
*/
SortedSet<Integer> getSupportedVersions();
/**
* Gets if the server uses spigot
* <p>
* Note: Will only work after ViaVersion load
*
* @return True if spigot
*/
boolean isSpigot();
/**
* Gets if protocol support is also being used.
*
* @return True if it is being used.
*/
boolean isProtocolSupport();
}
@@ -1,13 +1,8 @@
package us.myles.ViaVersion.api;
public interface ViaVersionConfig {
import java.util.List;
/**
* Get if global debug is enabled
*
* @return true if debug is enabled
*/
boolean isDebug();
public interface ViaVersionConfig {
/**
* Get if the plugin should check for updates
@@ -24,14 +19,14 @@ 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 clients are shown the new death message on the death screen
* Get if 1.9 & 1.10 clients are shown the new death message on the death screen
*
* @return true if enabled
*/
@@ -45,28 +40,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
@@ -78,6 +73,7 @@ public interface ViaVersionConfig {
*
* @return true if boss bar patching is enabled
*/
@Deprecated
boolean isUnknownEntitiesSuppressed();
/**
@@ -99,6 +95,7 @@ public interface ViaVersionConfig {
*
* @return true if it is enabled.
*/
@Deprecated
boolean isBlockBreakPatch();
/**
@@ -111,7 +108,7 @@ public interface ViaVersionConfig {
/**
* Get the kick message sent if the user hits the max packets per second.
*
* @return Kick message, with colour codes using '&'
* @return Kick message, with colour codes using '&amp;'
*/
String getMaxPPSKickMessage();
@@ -139,7 +136,7 @@ public interface ViaVersionConfig {
/**
* Get the kick message sent if the user goes over the warnings in the interval
*
* @return Kick message, with colour codes using '&'
* @return Kick message, with colour codes using '&amp;'
*/
String getMaxWarningsKickMessage();
@@ -149,4 +146,67 @@ public interface ViaVersionConfig {
* @return A boolean
*/
boolean isAntiXRay();
/**
* Send supported versions in the status response packet
*
* @return If true, enabled
*/
boolean isSendSupportedVersions();
/**
* Stimulate the player tick
*
* @return if true, enabled
*/
boolean isStimulatePlayerTick();
/**
* Use the item cache to prevent high resource usage
*
* @return if true, enabled
*/
boolean isItemCache();
/**
* Use the NMS player ticking
*
* @return if true, enabled
*/
boolean isNMSPlayerTicking();
/**
* Replace extended pistons on 1.10 chunk loading.
*
* @return True if to replace them
*/
boolean isReplacePistons();
/**
* Get the id for replacing extended pistons.
*
* @return The integer id
*/
int getPistonReplacementId();
/**
* Force json transform
*
* @return True if enabled
*/
boolean isForceJsonTransform();
/**
* Get the blocked protocols
*
* @return An Integer list
*/
List<Integer> getBlockedProtocols();
/**
* Get the custom disconnect message
*
* @return Disconnect message
*/
String getBlockedDisconnectMsg();
}
@@ -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,13 +62,15 @@ 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);
@@ -73,7 +78,7 @@ public interface BossBar {
* Show the bossbar to a player (uuid)
*
* @param player uuid of the player
* @return the BossBar instance
* @return The BossBar object
*/
BossBar addPlayer(UUID player);
@@ -81,34 +86,37 @@ public interface BossBar {
* add multiple players
*
* @param players list of players
* @return the bossbar instance
* @return The BossBar object
*/
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);
@@ -121,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";
@@ -1,10 +1,9 @@
package us.myles.ViaVersion.api.data;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter(AccessLevel.PROTECTED)
@Getter
@AllArgsConstructor
public class StoredObject {
private UserConnection user;
@@ -1,6 +1,7 @@
package us.myles.ViaVersion.api.data;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.socket.SocketChannel;
import lombok.Data;
@@ -39,6 +40,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) {
@@ -84,6 +86,17 @@ public class UserConnection {
}
}
/**
* Send a raw packet to the player with returning the future
*
* @param packet The raw packet to send
* @return ChannelFuture of the packet being sent
*/
public ChannelFuture sendRawPacketFuture(final ByteBuf packet) {
final ChannelHandler handler = channel.pipeline().get("encoder");
return channel.pipeline().context(handler).writeAndFlush(packet);
}
/**
* Send a raw packet to the player (netty thread)
*
@@ -102,6 +115,8 @@ public class UserConnection {
/**
* Used for incrementing the number of packets received from the client
*
* @return True if the interval has reset
*/
public boolean incrementReceived() {
// handle stats
@@ -125,8 +140,8 @@ public class UserConnection {
* @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;
if (!getChannel().isOpen()) return;
if (pendingDisconnect) return;
pendingDisconnect = true;
if (get(ProtocolInfo.class).getUuid() != null) {
final UUID uuid = get(ProtocolInfo.class).getUuid();
@@ -1,38 +1,17 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
public interface Chunk {
int getX();
@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;
int getZ();
/**
* 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;
}
ChunkSection[] getSections();
/**
* Does this chunk have biome data
*
* @return True if the chunk has biome data
*/
public boolean hasBiomeData() {
return biomeData != null && groundUp;
}
boolean isGroundUp();
boolean isBiomeData();
byte[] getBiomeData();
int getBitmask();
}
@@ -1,190 +1,15 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
public interface ChunkSection {
int getBlockId(int x, int y, int z);
public class ChunkSection {
/**
* Size (dimensions) of blocks in a chunk 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.
*/
void writeBlocks(ByteBuf output) throws Exception;
private final List<Integer> palette = Lists.newArrayList();
private final int[] blocks;
private final NibbleArray blockLight;
private NibbleArray skyLight;
void writeBlockLight(ByteBuf output) throws Exception;
public ChunkSection() {
this.blocks = new int[SIZE];
this.blockLight = new NibbleArray(SIZE);
palette.add(0); // AIR
}
boolean hasSkyLight();
/**
* Set a block in the chunk
*
* @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);
}
/**
* Set a block in the chunk 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 chunk section.
*
* @return Amount of bytes sent by this section
*/
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;
}
void writeSkyLight(ByteBuf output) throws Exception;
}
@@ -69,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()) {
@@ -94,20 +92,21 @@ public class ProtocolPipeline extends Protocol {
} else {
type = PacketType.findNewPacket(state, direction, originalID);
}
if (type != null) {
// Filter :) This would be not hard coded too, sorry :(
if (type == PacketType.PLAY_CHUNK_DATA) return;
if (type == PacketType.PLAY_TIME_UPDATE) return;
if (type == PacketType.PLAY_KEEP_ALIVE) return;
if (type == PacketType.PLAY_KEEP_ALIVE_REQUEST) return;
if (type == PacketType.PLAY_ENTITY_LOOK_MOVE) return;
if (type == PacketType.PLAY_ENTITY_LOOK) return;
if (type == PacketType.PLAY_ENTITY_RELATIVE_MOVE) return;
if (type == PacketType.PLAY_PLAYER_POSITION_LOOK_REQUEST) return;
if (type == PacketType.PLAY_PLAYER_LOOK_REQUEST) return;
if (type == PacketType.PLAY_PLAYER_POSITION_REQUEST) return;
// 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();
@@ -1,23 +1,28 @@
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_9_1_2to1_9_3.Protocol1_9_1_2TO1_9_3;
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<>();
public static final Protocol BASE_PROTOCOL = new BaseProtocol();
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
@@ -28,7 +33,8 @@ public class ProtocolRegistry {
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(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9_3.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());
}
@@ -54,11 +60,34 @@ 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());
}
}
/**
* Get the versions compatible with the server.
*
* @return Read-only set of the versions.
*/
public static SortedSet<Integer> getSupportedVersions() {
return Collections.unmodifiableSortedSet(new TreeSet<>(supportedVersions));
}
/**
* Check if this plugin is useful to the server.
*
@@ -90,7 +119,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 (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
@@ -1,14 +1,24 @@
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;
@@ -16,22 +26,36 @@ public class ProtocolVersion {
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"));
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) {
@@ -46,7 +70,25 @@ public class ProtocolVersion {
}
}
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));
@@ -59,8 +59,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 +80,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
@@ -6,7 +6,7 @@ import us.myles.ViaVersion.api.type.TypeConverter;
public class UnsignedByteType extends Type<Short> implements TypeConverter<Short> {
public UnsignedByteType() {
super(Short.class);
super("Unsigned Byte", Short.class);
}
@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);
}
}
@@ -3,7 +3,6 @@ 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.classgenerator.HandlerConstructor;
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
import us.myles.ViaVersion.handlers.ViaEncodeHandler;
@@ -3,6 +3,8 @@ 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;
@@ -10,72 +12,93 @@ 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()) return; // Use Basic Handler as not needed.
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()));
}
try {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new LoaderClassPath(ClassGenerator.class.getClassLoader()));
// Generate the classes
transformSuperclass(pool, ViaDecodeHandler.class, ReflectionUtil.nms("PacketDecoder"));
transformSuperclass(pool, ViaEncodeHandler.class, ReflectionUtil.nms("PacketEncoder"));
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));
// Implement Constructor
CtClass generated = pool.makeClass("us.myles.ViaVersion.classgenerator.generated.GeneratedConstructor");
CtClass handlerInterface = pool.get(HandlerConstructor.class.getName());
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();
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 transformSuperclass(ClassPool pool, Class input, Class superclass) {
private static Class addSpigotCompatibility(ClassPool pool, Class input, Class superclass) {
String newName = "us.myles.ViaVersion.classgenerator.generated." + input.getSimpleName();
try {
CtClass toExtend = pool.get(superclass.getName());
CtClass generated = pool.getAndRename(input.getName(), newName);
generated.setSuperclass(toExtend);
// 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);
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) {
@@ -85,4 +108,58 @@ public class ClassGenerator {
}
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;
}
}
@@ -6,5 +6,6 @@ import us.myles.ViaVersion.api.data.UserConnection;
public interface HandlerConstructor {
public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder);
public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder);
}
@@ -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"));
@@ -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;
}
}
@@ -56,6 +56,9 @@ public class PPSSubCmd extends ViaSubCommand {
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();
@@ -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;
}
@@ -38,8 +38,8 @@ public class ViaDecodeHandler extends ByteToMessageDecoder {
// Increment received
boolean second = info.incrementReceived();
// Check PPS
if(second) {
if (((ViaVersionPlugin) ViaVersion.getConfig()).handlePPS(info))
if (second) {
if (((ViaVersionPlugin) ViaVersion.getInstance()).handlePPS(info))
return;
}
@@ -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 :) */
@@ -1,5 +1,9 @@
package us.myles.ViaVersion.protocols.base;
import io.netty.channel.ChannelFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
@@ -19,7 +23,9 @@ 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 us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import java.util.List;
import java.util.UUID;
@@ -30,6 +36,8 @@ public class BaseProtocol extends Protocol {
@Override
protected void registerPackets() {
/* Outgoing Packets */
// Status Response Packet
registerOutgoing(State.STATUS, 0x00, 0x00, new PacketRemapper() { // Status Response Packet
@Override
public void registerMap() {
@@ -42,18 +50,26 @@ public class BaseProtocol extends Protocol {
try {
JSONObject json = (JSONObject) new JSONParser().parse(originalStatus);
JSONObject version = (JSONObject) json.get("version");
if (ProtocolRegistry.SERVER_PROTOCOL == -1) {
Long original = (Long) version.get("protocol");
ProtocolRegistry.SERVER_PROTOCOL = original.intValue();
}
int protocolVersion = ((Long) version.get("protocol")).intValue();
if (ViaVersion.getConfig().isSendSupportedVersions()) //Send supported versions
version.put("supportedVersions", ViaVersion.getInstance().getSupportedVersions());
if (ProtocolRegistry.SERVER_PROTOCOL == -1) // Set the Server protocol if the detection on startup failed
ProtocolRegistry.SERVER_PROTOCOL = protocolVersion;
List<Pair<Integer, Protocol>> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), ProtocolRegistry.SERVER_PROTOCOL);
if (protocols != null) {
if ((long) version.get("protocol") != 9999) //Fix serverlistplus
if (protocolVersion != 9999) //Fix ServerListPlus
version.put("protocol", info.getProtocolVersion());
} else {
// not compatible :(, *plays very sad violin*
wrapper.user().setActive(false);
}
if (ViaVersion.getConfig().getBlockedProtocols().contains(info.getProtocolVersion()))
version.put("protocol", -1); // Show blocked versions as outdated
wrapper.set(Type.STRING, 0, json.toJSONString()); // Update value
} catch (ParseException e) {
e.printStackTrace();
@@ -85,6 +101,10 @@ public class BaseProtocol extends Protocol {
info.setUsername(wrapper.get(Type.STRING, 1));
// Add to ported clients
((ViaVersionPlugin) ViaVersion.getInstance()).addPortedClient(wrapper.user());
if (info.getPipeline().pipes().size() == 1 && info.getPipeline().pipes().get(0).getClass() == BaseProtocol.class) // Only base protocol
wrapper.user().setActive(false);
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}",
@@ -127,11 +147,6 @@ public class BaseProtocol extends Protocol {
pipeline.add(prot.getValue());
}
wrapper.set(Type.VAR_INT, 0, ProtocolRegistry.SERVER_PROTOCOL);
} else {
if (state == 2) {
// not compatible :(, *plays very sad violin*
wrapper.user().setActive(false);
}
}
// Change state
@@ -148,7 +163,34 @@ public class BaseProtocol extends Protocol {
registerIncoming(State.STATUS, 0x00, 0x00); // Status Request Packet
registerIncoming(State.STATUS, 0x01, 0x01); // Status Ping Packet
registerIncoming(State.LOGIN, 0x00, 0x00); // Login Start Packet
// Login Start Packet
registerIncoming(State.LOGIN, 0x00, 0x00, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(final PacketWrapper wrapper) throws Exception {
int protocol = wrapper.user().get(ProtocolInfo.class).getProtocolVersion();
if (ViaVersion.getConfig().getBlockedProtocols().contains(protocol)) {
if (!wrapper.user().getChannel().isOpen()) return;
PacketWrapper disconnectPacket = new PacketWrapper(0x00, null, wrapper.user()); // Disconnect Packet
Protocol1_9TO1_8.FIX_JSON.write(disconnectPacket, ChatColor.translateAlternateColorCodes('&', ViaVersion.getConfig().getBlockedDisconnectMsg()));
wrapper.cancel(); // cancel current
// Send and close
ChannelFuture future = disconnectPacket.sendFuture(BaseProtocol.class);
future.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
wrapper.user().getChannel().close();
}
});
}
}
});
}
}); // Login Start Packet
registerIncoming(State.LOGIN, 0x01, 0x01); // Encryption Response Packet
}
@@ -168,4 +210,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);
}
}
@@ -1,19 +0,0 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import java.util.List;
@Data
@AllArgsConstructor
public class Chunk1_9_3 {
private int x;
private int z;
private boolean groundUp;
private int bitmask;
private byte[] sections;
List<CompoundTag> blockEntities;
}
@@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3;
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4;
import lombok.Getter;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
@@ -26,9 +26,6 @@ public class BlockEntity {
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) {
@@ -45,25 +42,15 @@ public class BlockEntity {
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);
//Sorry, the PacketWrapper class wil handle this in the future
//TODO let the packetwrapper class handle it
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);
}
updateBlockEntity(pos, (short) newId, tag, connection);
} catch (Exception e) {
if(ViaVersion.getInstance().isDebug()) {
if (ViaVersion.getInstance().isDebug()) {
System.out.println("Block Entity: " + e.getMessage() + ": " + tag);
}
}
@@ -75,14 +62,6 @@ public class BlockEntity {
wrapper.write(Type.POSITION, pos);
wrapper.write(Type.UNSIGNED_BYTE, id);
wrapper.write(Type.NBT, tag);
wrapper.send();
}
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();
wrapper.send(Protocol1_9_1_2TO1_9_3_4.class, false);
}
}
@@ -0,0 +1,29 @@
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 ChunkSection1_9_3_4[] sections;
List<CompoundTag> blockEntities;
@Override
public boolean isBiomeData() {
return false;
}
@Override
public byte[] getBiomeData() {
return new byte[0];
}
}
@@ -1,19 +1,21 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3;
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_3Type extends Type<Chunk1_9_3> {
public Chunk1_9_3Type() {
super("1.9.3 Chunk", Chunk1_9_3.class);
public class Chunk1_9_3_4Type extends BaseChunkType {
public Chunk1_9_3_4Type() {
super("1.9.3 Chunk");
}
@Override
public Chunk1_9_3 read(ByteBuf input) throws Exception {
public Chunk read(ByteBuf input) throws Exception {
int chunkX = input.readInt();
int chunkZ = input.readInt();
@@ -29,19 +31,23 @@ public class Chunk1_9_3Type extends Type<Chunk1_9_3> {
for (int i = 0; i < blockEntities; i++) {
nbtData.add(Type.NBT.read(input));
}
return new Chunk1_9_3(chunkX, chunkZ, groundUp, primaryBitmask, sections, nbtData);
return new Chunk1_9_3_4(chunkX, chunkZ, groundUp, primaryBitmask, new ChunkSection1_9_3_4[] {new ChunkSection1_9_3_4(sections)}, nbtData);
}
@Override
public void write(ByteBuf buffer, Chunk1_9_3 chunk) throws Exception {
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());
Type.VAR_INT.write(buffer, chunk.getSections()[0].getData().length);
buffer.writeBytes(chunk.getSections()[0].getData());
// no block entities as it's earlier
}
@@ -0,0 +1,41 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
public class ChunkSection1_9_3_4 implements ChunkSection {
private final byte[] data;
public ChunkSection1_9_3_4(byte[] data) {
this.data = data;
}
public byte[] getData() {
return data;
}
@Override
public int getBlockId(int x, int y, int z) {
throw new UnsupportedOperationException("Invalid chunk type!");
}
@Override
public void writeBlocks(ByteBuf output) throws Exception {
throw new UnsupportedOperationException("Invalid chunk type!");
}
@Override
public void writeBlockLight(ByteBuf output) throws Exception {
throw new UnsupportedOperationException("Invalid chunk type!");
}
@Override
public boolean hasSkyLight() {
throw new UnsupportedOperationException("Invalid chunk type!");
}
@Override
public void writeSkyLight(ByteBuf output) throws Exception {
throw new UnsupportedOperationException("Invalid chunk type!");
}
}
@@ -1,17 +1,18 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3;
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 extends Protocol {
public static Type<Chunk1_9_3> CHUNK = new Chunk1_9_3Type();
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() {
@@ -56,8 +57,8 @@ public class Protocol1_9_1_2TO1_9_3 extends Protocol {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Chunk1_9_3 chunk = wrapper.passthrough(CHUNK);
BlockEntity.handle(chunk.getBlockEntities(), wrapper.user());
Chunk chunk = wrapper.passthrough(CHUNK);
BlockEntity.handle(((Chunk1_9_3_4) chunk).getBlockEntities(), wrapper.user());
}
});
}
@@ -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;
@@ -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,108 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.bukkit.World;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.ViaVersion;
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.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import java.util.ArrayList;
import java.util.BitSet;
public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
public Chunk1_9_1_2Type(ClientWorld clientWorld) {
super(clientWorld, Chunk.class);
}
@Override
public Chunk read(ByteBuf input, ClientWorld world) throws Exception {
boolean replacePistons = world.getUser().get(ProtocolInfo.class).getPipeline().contains(Protocol1_10To1_9_3_4.class) && ViaVersion.getConfig().isReplacePistons();
int replacementId = ViaVersion.getConfig().getPistonReplacementId();
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;
section.readBlocks(input);
section.readBlockLight(input);
if (world.getEnvironment() == World.Environment.NORMAL) {
section.readSkyLight(input);
}
if (replacePistons) {
if (section.getPalette().contains(36)) {
section.getPalette().set(section.getPalette().indexOf(36), replacementId);
}
}
}
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, ClientWorld world, Chunk input) throws Exception {
Chunk chunk = 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++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set
section.writeBlocks(buf);
section.writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
section.writeSkyLight(buf);
}
buf.readerIndex(0);
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());
}
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
}
@@ -0,0 +1,293 @@
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 lombok.Getter;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
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 implements ChunkSection {
/**
* 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.
*/
@Getter
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.get(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 y << 8 | z << 4 | x;
}
/**
* Read blocks from input stream.
* This reads all the block related data:
* <ul>
* <li>Block length/palette type</li>
* <li>Palette</li>
* <li>Block hashes/palette reference</li>
* </ul>
*
* @param input The buffer to read from.
* @throws Exception
*/
public void readBlocks(ByteBuf input) throws Exception {
palette.clear();
// Reaad bits per block
int bitsPerBlock = input.readUnsignedByte();
long maxEntryValue = (1L << bitsPerBlock) - 1;
if (bitsPerBlock == 0) {
bitsPerBlock = 13;
}
if (bitsPerBlock < 4) {
bitsPerBlock = 4;
}
if (bitsPerBlock > 8) {
bitsPerBlock = 13;
}
int paletteLength = Type.VAR_INT.read(input);
// Read palette
for (int i = 0; i < paletteLength; i++) {
if (bitsPerBlock != 13) {
palette.add(Type.VAR_INT.read(input));
} else {
Type.VAR_INT.read(input);
}
}
// Read blocks
Long[] blockData = Type.LONG_ARRAY.read(input);
if (blockData.length > 0) {
for (int i = 0; i < blocks.length; i++) {
int bitIndex = i * bitsPerBlock;
int startIndex = bitIndex / 64;
int endIndex = ((i + 1) * bitsPerBlock - 1) / 64;
int startBitSubIndex = bitIndex % 64;
int val;
if (startIndex == endIndex) {
val = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue);
} else {
int endBitSubIndex = 64 - startBitSubIndex;
val = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue);
}
if (bitsPerBlock == 13) {
int type = val >> 4;
int data = val & 0xF;
setBlock(i, type, data);
} else {
blocks[i] = val;
}
}
}
}
/**
* Read block light from buffer.
*
* @param input The buffer to read from
*/
public void readBlockLight(ByteBuf input) {
byte[] handle = new byte[LIGHT_LENGTH];
input.readBytes(handle);
blockLight.setHandle(handle);
}
/**
* Read sky light from buffer.
* Note: Only sent in overworld!
*
* @param input The buffer to read from
*/
public void readSkyLight(ByteBuf input) {
byte[] handle = new byte[LIGHT_LENGTH];
input.readBytes(handle);
if (skyLight != null) {
skyLight.setHandle(handle);
return;
}
this.skyLight = new NibbleArray(handle);
}
/**
* 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(Arrays.asList(Material.PISTON_BASE, Material.PISTON_EXTENSION, Material.PISTON_STICKY_BASE, Material.PISTON_MOVING_PIECE), "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");
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;
}
}
@@ -6,11 +6,17 @@ 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.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
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_9_3to1_9_1_2.storage.ClientWorld;
import java.util.ArrayList;
import java.util.List;
public class Protocol1_9_3TO1_9_1_2 extends Protocol {
@Override
@@ -24,7 +30,7 @@ public class Protocol1_9_3TO1_9_1_2 extends Protocol {
registerOutgoing(State.PLAY, 0x4B, 0x4A); //Entity properties
registerOutgoing(State.PLAY, 0x4C, 0x4B); //Entity effect
//Sign update
// Sign update packet
registerOutgoing(State.PLAY, 0x46, -1, new PacketRemapper() {
@Override
public void registerMap() {
@@ -60,14 +66,72 @@ public class Protocol1_9_3TO1_9_1_2 extends Protocol {
}
});
// Chunk packet
registerOutgoing(State.PLAY, 0x20, 0x20, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
wrapper.passthroughAll();
wrapper.write(Type.VAR_INT, 0);
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
Chunk1_9_1_2Type type = new Chunk1_9_1_2Type(clientWorld);
Chunk chunk = wrapper.passthrough(type);
List<CompoundTag> tags = new ArrayList<>();
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection 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)) {
tags.add(FakeTileEntity.getFromBlock(x + (chunk.getX() << 4), y + (i << 4), z + (chunk.getZ() << 4), block));
}
}
}
}
}
wrapper.write(Type.NBT_ARRAY, tags.toArray(new CompoundTag[0]));
}
});
}
});
// Join (save dimension id)
registerOutgoing(State.PLAY, 0x23, 0x23, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Entity ID
map(Type.UNSIGNED_BYTE); // 1 - Gamemode
map(Type.INT); // 2 - Dimension
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientChunks = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 1);
clientChunks.setEnvironment(dimensionId);
}
});
}
});
// Respawn (save dimension id)
registerOutgoing(State.PLAY, 0x33, 0x33, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Dimension ID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 0);
clientWorld.setEnvironment(dimensionId);
}
});
}
@@ -75,7 +139,7 @@ public class Protocol1_9_3TO1_9_1_2 extends Protocol {
}
@Override
public void init(UserConnection userConnection) {
public void init(UserConnection user) {
user.put(new ClientWorld(user));
}
}
@@ -0,0 +1,32 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage;
import lombok.Getter;
import org.bukkit.World;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
@Getter
public class ClientWorld extends StoredObject {
private World.Environment environment;
public ClientWorld(UserConnection user) {
super(user);
}
public void setEnvironment(int environmentId) {
this.environment = getEnvFromId(environmentId);
}
private World.Environment getEnvFromId(int id) {
switch(id) {
case -1:
return World.Environment.NETHER;
case 0:
return World.Environment.NORMAL;
case 1:
return World.Environment.THE_END;
default:
throw new IllegalArgumentException("Invalid environment id:" + id);
}
}
}
@@ -213,11 +213,9 @@ public class ItemRewriter {
item.setId((short) 438); // splash id
item.setData((short) (item.getData() - 8192));
}
if (POTION_ID_TO_NAME.containsKey((int) item.getData())) {
String name = POTION_ID_TO_NAME.get((int) item.getData());
StringTag potion = new StringTag("Potion", "minecraft:" + name);
tag.put(potion);
}
String name = potionNameFromDamage(item.getData());
StringTag potion = new StringTag("Potion", "minecraft:" + name);
tag.put(potion);
item.setTag(tag);
item.setData((short) 0);
}
@@ -245,10 +243,113 @@ public class ItemRewriter {
}
}
public static String potionNameFromDamage(short damage) {
String cached = POTION_ID_TO_NAME.get((int) damage);
if (cached != null) {
return cached;
}
if (damage == 0) {
return "water";
}
int effect = damage & 0xF;
int name = damage & 0x3F;
boolean enhanced = (damage & 0x20) > 0;
boolean extended = (damage & 0x40) > 0;
boolean canEnhance = true;
boolean canExtend = true;
String id;
switch (effect) {
case 1:
id = "regeneration";
break;
case 2:
id = "swiftness";
break;
case 3:
id = "fire_resistance";
canEnhance = false;
break;
case 4:
id = "poison";
break;
case 5:
id = "healing";
canExtend = false;
break;
case 6:
id = "night_vision";
canEnhance = false;
break;
case 8:
id = "weakness";
canEnhance = false;
break;
case 9:
id = "strength";
break;
case 10:
id = "slowness";
canEnhance = false;
break;
case 11:
id = "leaping";
break;
case 12:
id = "harming";
canExtend = false;
break;
case 13:
id = "water_breathing";
canEnhance = false;
break;
case 14:
id = "invisibility";
canEnhance = false;
break;
default:
canEnhance = false;
canExtend = false;
switch (name) {
case 0:
id = "mundane";
break;
case 16:
id = "awkward";
break;
case 32:
id = "thick";
break;
default:
id = "empty";
}
}
if (effect > 0) {
if (canEnhance && enhanced) {
id = "strong_" + id;
} else if (canExtend && extended) {
id = "long_" + id;
}
}
return id;
}
public static int getNewEffectID(int oldID) {
if (oldID >= 16384) {
oldID -= 8192;
}
if (POTION_INDEX.containsKey(oldID)) {
return POTION_INDEX.get(oldID);
}
oldID = POTION_NAME_TO_ID.get(potionNameFromDamage((short) oldID));
return POTION_INDEX.containsKey(oldID) ? POTION_INDEX.get(oldID) : 0;
}
@@ -41,9 +41,7 @@ public class Protocol1_9TO1_8 extends Protocol {
line = "{\"text\":\"\"}";
} else {
if ((!line.startsWith("\"") || !line.endsWith("\"")) && (!line.startsWith("{") || !line.endsWith("}"))) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("text", line);
return gson.toJson(jsonObject);
return constructJson(line);
}
if (line.startsWith("\"") && line.endsWith("\"")) {
line = "{\"text\":" + line + "}";
@@ -52,12 +50,22 @@ public class Protocol1_9TO1_8 extends Protocol {
try {
gson.fromJson(line, JsonObject.class);
} catch (Exception e) {
System.out.println("Invalid JSON String: \"" + line + "\" Please report this issue to the ViaVersion Github: " + e.getMessage());
return "{\"text\":\"\"}";
if (ViaVersion.getConfig().isForceJsonTransform()) {
return constructJson(line);
} else {
System.out.println("Invalid JSON String: \"" + line + "\" Please report this issue to the ViaVersion Github: " + e.getMessage());
return "{\"text\":\"\"}";
}
}
return line;
}
private static String constructJson(String text) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("text", text);
return gson.toJson(jsonObject);
}
public static Item getHandItem(final UserConnection info) {
if (HandItemCache.CACHE) {
return HandItemCache.getHandItem(info.get(ProtocolInfo.class).getUuid());
@@ -98,13 +106,13 @@ public class Protocol1_9TO1_8 extends Protocol {
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))
if (plugin.getConf().isStimulatePlayerTick())
new ViaIdleThread(plugin.getPortedPlayers()).runTaskTimer(plugin, 1L, 1L); // Updates player's idle status
if (plugin.getConfig().getBoolean("item-cache", true)) {
if (plugin.getConf().isItemCache()) {
new HandItemCache().runTaskTimerAsynchronously(plugin, 2L, 2L); // Updates player's items :)
HandItemCache.CACHE = true;
}
@@ -4,7 +4,6 @@ import io.netty.channel.ChannelHandlerContext;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
@@ -30,7 +29,7 @@ public class ViaIdleThread extends BukkitRunnable {
private Method handleFlying;
public ViaIdleThread(Map<UUID, UserConnection> portedPlayers) {
USE_NMS = ((ViaVersionPlugin) ViaVersion.getInstance()).getConfig().getBoolean("nms-player-ticking", true);
USE_NMS = ViaVersion.getConfig().isNMSPlayerTicking();
this.portedPlayers = portedPlayers;
Class<?> idlePacketClass;
@@ -0,0 +1,49 @@
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;
}
@Override
public boolean isBiomeData() {
return biomeData != null;
}
@Override
public int getBitmask() {
return primaryBitmask;
}
}
@@ -0,0 +1,198 @@
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.ChunkSection;
import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
public class ChunkSection1_9to1_8 implements ChunkSection {
/**
* 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_9to1_8() {
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.get(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 y << 8 | z << 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;
}
}
@@ -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,9 +15,9 @@ 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.protocol.ProtocolVersion;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.ArmorType;
@@ -40,24 +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());
int protocol = userConnection.get(ProtocolInfo.class).getProtocolVersion();
ByteBuf buf = Unpooled.buffer();
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
//TODO possibility to send packets by Protocol version, to let the transformer do the work
Type.VAR_INT.write(buf, (protocol >= ProtocolVersion.v1_9_3.getId()) ? 0x4A : 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) {
buf.release();
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -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,26 +98,32 @@ 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 {
@@ -10,6 +10,7 @@ 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;
@@ -22,7 +23,7 @@ public class DeathListener implements Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onDeath(PlayerDeathEvent e) {
Player p = e.getEntity();
if (plugin.isShowNewDeathMessages() && checkGamerule(p.getWorld()) && e.getDeathMessage() != null && checkPipeline(p)) {
if (ViaVersion.getConfig().isShowNewDeathMessages() && checkGamerule(p.getWorld()) && e.getDeathMessage() != null && checkPipeline(p)) {
sendPacket(p, e.getDeathMessage());
}
}
@@ -54,7 +55,7 @@ public class DeathListener implements Listener {
wrapper.write(Type.VAR_INT, p.getEntityId());
wrapper.write(Type.INT, p.getEntityId());
Protocol1_9TO1_8.FIX_JSON.write(wrapper, msg);
wrapper.send();
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
wrapper.clearInputBuffer();
@@ -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,9 +245,6 @@ 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();
}
});
@@ -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);
@@ -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
@@ -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);
}
});
@@ -297,7 +303,7 @@ public class SpawnPackets {
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;
@@ -16,6 +15,7 @@ import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
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;
@@ -115,7 +115,7 @@ 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));
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.passthrough(new ChunkType(clientChunks));
if (chunk.isUnloadPacket())
wrapper.setId(0x1D);
@@ -283,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();
// }
// });
}
});
@@ -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),
@@ -3,7 +3,10 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldType;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
@@ -26,10 +29,12 @@ public class ClientChunks extends StoredObject {
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");
if (ViaVersion.getInstance().isSpigot()) {
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);
}
}
@@ -51,17 +56,21 @@ public class ClientChunks extends StoredObject {
int[] zcoords = mapChunkBulkRef.getFieldValue("b", packet, int[].class);
Object[] chunkMaps = mapChunkBulkRef.getFieldValue("c", packet, Object[].class);
if (ViaVersion.getConfig().isAntiXRay()) { //Spigot anti-xray patch
Object world = mapChunkBulkRef.getFieldValue("world", 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);
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);
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);
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++) {
@@ -77,7 +86,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;
}
@@ -8,7 +8,6 @@ import lombok.Getter;
import lombok.Setter;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.boss.BossBar;
@@ -21,7 +20,9 @@ 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.*;
@@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit;
public class EntityTracker extends StoredObject {
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();
@@ -42,8 +44,6 @@ public class EntityTracker extends StoredObject {
@Setter
private boolean autoTeam = false;
@Setter
private Long lastPlaceBlock = -1L;
@Setter
private int entityID;
@Setter
private Position currentlyDigging = null;
@@ -75,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();
}
@@ -87,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) {
@@ -113,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)) {
@@ -138,11 +141,18 @@ public class EntityTracker extends StoredObject {
}
}
//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);
@@ -154,7 +164,7 @@ 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();
@@ -169,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);
@@ -181,7 +191,7 @@ public class EntityTracker extends StoredObject {
}
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);
@@ -195,7 +205,7 @@ public class EntityTracker extends StoredObject {
} 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;
@@ -246,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);
}
}
}
@@ -8,7 +8,7 @@ import us.myles.ViaVersion.api.minecraft.Position;
@Getter
public class PlaceBlockTracker extends StoredObject {
private long lastPlaceTimestamp = System.currentTimeMillis();
private long lastPlaceTimestamp = 0;
@Setter
private Position lastPlacedPosition = null;
@@ -3,10 +3,15 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.types;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.bukkit.Bukkit;
import us.myles.ViaVersion.api.ViaVersion;
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.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
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 +22,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,8 +42,16 @@ 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 {
boolean replacePistons = param.getUser().get(ProtocolInfo.class).getPipeline().contains(Protocol1_10To1_9_3_4.class) && ViaVersion.getConfig().isReplacePistons();
int replacementId = ViaVersion.getConfig().getPistonReplacementId();
int chunkX = input.readInt();
int chunkZ = input.readInt();
long chunkHash = toLong(chunkX, chunkZ);
@@ -48,7 +61,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 +72,35 @@ 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;
if (replacePistons && type == 36)
type = replacementId;
section.setBlock(j, type, data);
}
}
@@ -93,20 +108,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,15 +134,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 {
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());
@@ -137,12 +155,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));
@@ -153,5 +173,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 {
@@ -1,7 +1,9 @@
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;
@@ -24,6 +26,26 @@ public class Protocol1_9TO1_9_1 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) // 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
@@ -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);
}
}
@@ -0,0 +1,268 @@
package us.myles.ViaVersion.util;
import lombok.SneakyThrows;
import java.lang.reflect.Field;
import java.util.*;
/**
* Created by wea_ondara licensed under MIT
* Same license as in LICENSE
*
* Taken from:
* https://github.com/weaondara/BungeePerms/blob/master/src/main/java/net/alpenblock/bungeeperms/util/ConcurrentList.java
* @param <E> List Type
*/
public class ConcurrentList<E> extends ArrayList<E> {
private final Object lock = new Object();
@Override
public boolean add(E e) {
synchronized (lock) {
return super.add(e);
}
}
@Override
public void add(int index, E element) {
synchronized (lock) {
super.add(index, element);
}
}
@Override
public boolean addAll(Collection<? extends E> c) {
synchronized (lock) {
return super.addAll(c);
}
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
synchronized (lock) {
return super.addAll(index, c);
}
}
@Override
public void clear() {
synchronized (lock) {
super.clear();
}
}
@Override
@SneakyThrows
public Object clone() {
synchronized (lock) {
ConcurrentList<E> clist = (ConcurrentList<E>) super.clone();
clist.modCount = 0;
Field f = ArrayList.class.getDeclaredField("elementData");
f.setAccessible(true);
f.set(clist, Arrays.copyOf((Object[]) f.get(this), this.size()));
return clist;
}
}
@Override
public boolean contains(Object o) {
synchronized (lock) {
return super.contains(o);
}
}
@Override
public void ensureCapacity(int minCapacity) {
synchronized (lock) {
super.ensureCapacity(minCapacity);
}
}
@Override
public E get(int index) {
synchronized (lock) {
return super.get(index);
}
}
@Override
public int indexOf(Object o) {
synchronized (lock) {
return super.indexOf(o);
}
}
@Override
public int lastIndexOf(Object o) {
synchronized (lock) {
return super.lastIndexOf(o);
}
}
@Override
public E remove(int index) {
synchronized (lock) {
return super.remove(index);
}
}
@Override
public boolean remove(Object o) {
synchronized (lock) {
return super.remove(o);
}
}
@Override
public boolean removeAll(Collection<?> c) {
synchronized (lock) {
return super.removeAll(c);
}
}
@Override
public boolean retainAll(Collection<?> c) {
synchronized (lock) {
return super.retainAll(c);
}
}
@Override
public E set(int index, E element) {
synchronized (lock) {
return super.set(index, element);
}
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
synchronized (lock) {
return super.subList(fromIndex, toIndex);
}
}
@Override
public Object[] toArray() {
synchronized (lock) {
return super.toArray();
}
}
@Override
public <T> T[] toArray(T[] a) {
synchronized (lock) {
return super.toArray(a);
}
}
@Override
public void trimToSize() {
synchronized (lock) {
super.trimToSize();
}
}
@Override
public ListIterator<E> listIterator() {
return new ListItr(0);
}
@Override
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
protected int cursor;
protected int lastRet;
final ConcurrentList l;
public Itr() {
cursor = 0;
lastRet = -1;
l = (ConcurrentList) ConcurrentList.this.clone();
}
@Override
public boolean hasNext() {
return cursor < l.size();
}
@Override
public E next() {
int i = cursor;
if (i >= l.size()) {
throw new NoSuchElementException();
}
cursor = i + 1;
return (E) l.get(lastRet = i);
}
@Override
public void remove() {
if (lastRet < 0) {
throw new IllegalStateException();
}
l.remove(lastRet);
ConcurrentList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
}
}
public class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
@Override
public boolean hasPrevious() {
return cursor > 0;
}
@Override
public int nextIndex() {
return cursor;
}
@Override
public int previousIndex() {
return cursor - 1;
}
@Override
public E previous() {
int i = cursor - 1;
if (i < 0) {
throw new NoSuchElementException();
}
cursor = i;
return (E) l.get(lastRet = i);
}
@Override
public void set(E e) {
if (lastRet < 0) {
throw new IllegalStateException();
}
l.set(lastRet, e);
ConcurrentList.this.set(lastRet, e);
}
@Override
public void add(E e) {
int i = cursor;
l.add(i, e);
ConcurrentList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
}
}
}
@@ -1,11 +1,9 @@
package us.myles.ViaVersion.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.*;
public abstract class ListWrapper implements List {
public final Object lock = new Object();
private final List list;
public ListWrapper(List inputList) {
@@ -20,123 +18,169 @@ public abstract class ListWrapper implements List {
@Override
public synchronized int size() {
return this.list.size();
synchronized (lock) {
return this.list.size();
}
}
@Override
public synchronized boolean isEmpty() {
return this.list.isEmpty();
synchronized (lock) {
return this.list.isEmpty();
}
}
@Override
public synchronized boolean contains(Object o) {
return this.list.contains(o);
synchronized (lock) {
return this.list.contains(o);
}
}
@Override
public synchronized Iterator iterator() {
return this.list.iterator();
synchronized (lock) {
return listIterator();
}
}
@Override
public synchronized Object[] toArray() {
return this.list.toArray();
synchronized (lock) {
return this.list.toArray();
}
}
@Override
public synchronized boolean add(Object o) {
handleAdd(o);
return this.list.add(o);
synchronized (lock) {
handleAdd(o);
return this.list.add(o);
}
}
@Override
public synchronized boolean remove(Object o) {
return this.list.remove(o);
synchronized (lock) {
return this.list.remove(o);
}
}
@Override
public synchronized boolean addAll(Collection c) {
for (Object o : c) {
handleAdd(o);
synchronized (lock) {
for (Object o : c) {
handleAdd(o);
}
return this.list.addAll(c);
}
return this.list.addAll(c);
}
@Override
public synchronized boolean addAll(int index, Collection c) {
for (Object o : c) {
handleAdd(o);
synchronized (lock) {
for (Object o : c) {
handleAdd(o);
}
return this.list.addAll(index, c);
}
return this.list.addAll(index, c);
}
@Override
public synchronized void clear() {
this.list.clear();
synchronized (lock) {
this.list.clear();
}
}
@Override
public synchronized Object get(int index) {
return this.list.get(index);
synchronized (lock) {
return this.list.get(index);
}
}
@Override
public synchronized Object set(int index, Object element) {
return this.list.set(index, element);
synchronized (lock) {
return this.list.set(index, element);
}
}
@Override
public synchronized void add(int index, Object element) {
this.list.add(index, element);
synchronized (lock) {
this.list.add(index, element);
}
}
@Override
public synchronized Object remove(int index) {
return this.list.remove(index);
synchronized (lock) {
return this.list.remove(index);
}
}
@Override
public synchronized int indexOf(Object o) {
return this.list.indexOf(o);
synchronized (lock) {
return this.list.indexOf(o);
}
}
@Override
public synchronized int lastIndexOf(Object o) {
return this.list.lastIndexOf(o);
synchronized (lock) {
return this.list.lastIndexOf(o);
}
}
@Override
public synchronized ListIterator listIterator() {
return this.list.listIterator();
synchronized (lock) {
return this.list.listIterator();
}
}
@Override
public synchronized ListIterator listIterator(int index) {
return this.list.listIterator(index);
synchronized (lock) {
return this.list.listIterator(index);
}
}
@Override
public synchronized List subList(int fromIndex, int toIndex) {
return this.list.subList(fromIndex, toIndex);
synchronized (lock) {
return this.list.subList(fromIndex, toIndex);
}
}
@Override
public synchronized boolean retainAll(Collection c) {
return this.list.retainAll(c);
synchronized (lock) {
return this.list.retainAll(c);
}
}
@Override
public synchronized boolean removeAll(Collection c) {
return this.list.removeAll(c);
synchronized (lock) {
return this.list.removeAll(c);
}
}
@Override
public synchronized boolean containsAll(Collection c) {
return this.list.containsAll(c);
synchronized (lock) {
return this.list.containsAll(c);
}
}
@Override
public synchronized Object[] toArray(Object[] a) {
return this.list.toArray(a);
synchronized (lock) {
return this.list.toArray(a);
}
}
}
@@ -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;
}
}
+4
View File
@@ -0,0 +1,4 @@
name: ViaVersion
main: us.myles.ViaVersion.Bungee
author: _MylesC
version: ${project.version}
+60 -31
View File
@@ -1,5 +1,52 @@
# 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
# Block specific Minecraft protocols that ViaVersion allows
# List of all Minecraft protocol versions: http://wiki.vg/Protocol_version_numbers
block-protocols: []
# Change the blocked disconnect message
block-disconnect-msg: "You are using an unsupported Minecraft version!"
#
#----------------------------------------------------------#
# GLOBAL PACKET LIMITER #
#----------------------------------------------------------#
#
#
# Packets Per Second (PPS) limiter (Use -1 on max-pps and tracking-period to disable)
# 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-period"
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,50 +54,32 @@ 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
# Show the new death messages for 1.9 on the death screen
# Show the new death messages for 1.9 & 1.10 on the death screen
use-new-deathmessages: false
# 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
# Our patch for block breaking issue, if you have issues with block updates then disable this.
block-break-patch: true
# 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
# 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 -1 to disable.
max-pps: 400
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, :("
# Patch the Anti xray to work on 1.9 (If your server is 1.8) This can cost more performance, so disable it if you don't use it.
anti-xray-patch: 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
# Should we replace extended pistons to fix 1.10.1 (Only on chunk load)
replace-pistons: false
# What id should we replace with, default is air. (careful of players getting stuck standing on them)
replacement-piston-id: 0
# Force the string -> json transform
force-json-transform: false
+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.