Compare commits

...
96 Commits
Author SHA1 Message Date
Myles 25a7915c7f 0.9.9 Version 2016-08-25 16:52:34 +01:00
Myles 822d582074 Deprecate isPorted, we port all players use getPlayerVersion instead. 2016-08-24 20:40:53 +01:00
Matsv f8a73d5fc2 Merge branch 'master' of https://github.com/Matsv/ViaVersion into upmaster 2016-08-22 21:38:19 +02:00
Matsv a0ad97947d Add the configuration to the dump command, also add a toString method to the ProtocolVersion class. 2016-08-22 21:37:51 +02:00
Myles 6ea5d0849d Missed a bit off the README 2016-08-21 15:52:59 +01:00
Myles eba512ef0b Update README with IRC 2016-08-21 15:51:21 +01:00
Matsv 0f96c24c78 Merge branch 'master' of https://github.com/Matsv/ViaVersion into dumpthemall 2016-08-20 16:21:19 +02:00
Matsv f2e4c37cd9 Add ViaVersion dump command to get all the relevant info we need by bug reports. 2016-08-20 16:19:44 +02:00
Myles f159a2250c SemVer compatibility, also change up the messages a bit. 2016-08-20 13:47:09 +01:00
Myles ed311e4059 Clean up some messages to be more understandable. 2016-08-20 12:03:23 +01:00
AlfieC 6aae98a8a9 typo (#485) 2016-08-17 23:32:21 +02:00
Matsv 0f3daa5dc4 Merge branch 'chunkFix' of https://github.com/Matsv/ViaVersion into upmaster 2016-08-08 18:00:40 +02:00
Matsv f258cf8c89 Stop depending on versions for the ChunkTypes. fix cast exceptions by using a ChunkType in front of it. 2016-08-08 17:38:11 +02:00
Matsv ca759c056e Add some missing sounds 2016-08-07 17:53:08 +02:00
Matsv d715607826 Implement 1_8 writing 2016-08-05 20:17:39 +02:00
inventivetalent afca92d122 update requests to use /v2 (#479) 2016-08-04 20:42:03 +01:00
Myles ae37b34692 Snapshot 2016-08-01 14:07:33 +01:00
Myles 9e42a368e0 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-08-01 14:06:11 +01:00
Myles 27982efd29 Bump version to 0.9.8 2016-08-01 14:05:07 +01:00
Mason c3b343fbc8 Allow non-solid blocks to be placed at feet (#472) 2016-07-22 21:06:51 +01:00
Matsv 9bedb6c741 Move some classes to other packages 2016-07-17 12:00:46 +02:00
Matsv 873f4dd298 Give it some protection 2016-07-11 22:06:00 +02:00
Matsv 693990644f Rename the Meta(List)1_9_1_10Type to Metadata(List)1_9Type and cleanup the listeners 2016-07-11 20:27:44 +02:00
Matsv b2680d76a6 WIP cleanup 2016-07-11 15:52:06 +02:00
Matsv db935b454f Make the Metadata types version based and remove the NoGravity tag init since it doesn't seem needed. 2016-07-11 13:48:48 +02:00
Myles c207a9bfff Count other worlds as nether instead of imploding. 2016-07-10 12:35:46 +01:00
Myles f5bd041c7b Fix PacketListenerAPI load to be before Via, fixes #463. Tested with NickNamer 2016-07-09 15:21:17 +01:00
Myles 07d89b24af Custom reload message #459 2016-07-09 14:41:07 +01:00
Myles 1d0c0c527c Snapshot 2016-07-08 12:56:34 +01:00
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
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
80 changed files with 3123 additions and 876 deletions

No files matched your search

+10 -31
View File
@@ -1,50 +1,29 @@
# ViaVersion 0.9.2
# ViaVersion 0.9.9
[![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)
## This is an experimental branch for 1.10, we do not recommend you to use this on a production server
**Direct download link:** [Latest builds](https://ci.viaversion.com/job/ViaVersion%201.10%20DEV/)
IRC: [#viaversion](http://buzz.spi.gt/iris/?channels=viaversion) on irc.spi.gt for Support.
**Allows the connection of 1.9 clients to 1.8**
**1.10 clients on 1.8 / 1.9 servers**
**Allows the connection of higher client versions to lower server versions**
This took 100s hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
Allows 1.10, 1.9 on 1.8 server and 1.10 on a 1.9 server.
**User Docs:** https://docs.viaversion.com/display/VIAVERSION/
**Contributor Chat:** https://gitter.im/MylesIsCool/ViaVersion
Sources:
--------
**wiki.vg** (Used for various information, we also contribute back)
**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
**Burger** (See [PAaaS](https://github.com/Matsv/Paaas))
**OpenNBT** (used for slot rewriting)
Contributors:
--------
**Myself** (harhar)
**Matsv/StamBoom**
**HugoDaBosss**
**SanderGielisse**
**Paulomart**
**gigosaurus**
**fillefilip8**
**lenis0012**
Releases / Dev Builds:
--------
You can find official releases here:
@@ -67,5 +46,5 @@ License:
MIT License, see LICENSE for more details.
Thanks!
Special thanks to all our [Contributors](https://github.com/MylesIsCool/ViaVersion/graphs/contributors)
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>0.9.2</version>
<version>0.9.9</version>
<packaging>jar</packaging>
<name>ViaVersion</name>
@@ -4,6 +4,7 @@ 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;
@@ -87,7 +88,7 @@ public class ViaConfig implements ViaVersionConfig {
@Override
public double getHologramYOffset() {
return plugin.getConfig().getDouble("hologram-y", -1D);
return plugin.getConfig().getDouble("hologram-y", -0.96D);
}
@Override
@@ -150,8 +151,38 @@ public class ViaConfig implements ViaVersionConfig {
return plugin.getConfig().getBoolean("nms-player-ticking", true);
}
@Override
public boolean isReplacePistons() {
return plugin.getConfig().getBoolean("replace-pistons", false);
}
@Override
public int getPistonReplacementId() {
return plugin.getConfig().getInt("replacement-piston-id", 0);
}
public boolean isAutoTeam() {
// Collision has to be enabled first
return isPreventCollision() && plugin.getConfig().getBoolean("auto-team", true);
}
@Override
public boolean isForceJsonTransform() {
return plugin.getConfig().getBoolean("force-json-transform", false);
}
@Override
public List<Integer> getBlockedProtocols() {
return plugin.getConfig().getIntegerList("block-protocols");
}
@Override
public String getBlockedDisconnectMsg() {
return plugin.getConfig().getString("block-disconnect-msg", "You are using an unsupported Minecraft version!");
}
@Override
public String getReloadDisconnectMsg() {
return plugin.getConfig().getString("reload-disconnect-msg", "Server reload, please rejoin!");
}
}
@@ -7,6 +7,7 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import lombok.Getter;
import lombok.NonNull;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
@@ -20,6 +21,7 @@ import us.myles.ViaVersion.api.boss.BossStyle;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.boss.ViaBossBar;
import us.myles.ViaVersion.classgenerator.ClassGenerator;
import us.myles.ViaVersion.commands.ViaCommandHandler;
@@ -27,7 +29,9 @@ 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.ConcurrentList;
import us.myles.ViaVersion.util.ListWrapper;
import us.myles.ViaVersion.util.ProtocolSupportUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Field;
@@ -48,40 +52,59 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
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("Hooking into ProtocolSupport, to prevent issues!");
try {
patchLists();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onLoad() {
// Config magic
conf = new ViaConfig(this);
ViaVersion.setInstance(this);
// 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.");
for (Player player : Bukkit.getOnlinePlayers()) {
player.kickPlayer("Server reload, please rejoin!");
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', getConf().getReloadDisconnectMsg()));
}
} else {
getLogger().severe("ViaVersion is already loaded, this should work fine... Otherwise reboot the server!!!");
getLogger().severe("ViaVersion is already loaded, this should work fine. If you get any console errors, try rebooting.");
}
}
// Spigot detector
try {
Class.forName("org.spigotmc.SpigotConfig");
} catch (ClassNotFoundException e) {
spigot = false;
}
// Check if it's a spigot build with a protocol mod
try {
compatSpigotBuild = ReflectionUtil.nms("PacketEncoder").getDeclaredField("version") != null;
} catch (Exception e) {
compatSpigotBuild = false;
}
// Generate classes needed (only works if it's compat)
// Generate classes needed (only works if it's compat or ps)
ClassGenerator.generate();
lateBind = !isBinded();
@@ -103,9 +126,9 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
gatherProtocolVersion();
// Check if there are any pipes to this version
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
getLogger().info("ViaVersion detected protocol version: " + ProtocolRegistry.SERVER_PROTOCOL);
getLogger().info("ViaVersion detected server version: " + ProtocolVersion.getProtocol(ProtocolRegistry.SERVER_PROTOCOL));
if (!ProtocolRegistry.isWorkingPipe()) {
getLogger().warning("ViaVersion will not function on the current protocol.");
getLogger().warning("ViaVersion does not have any compatible versions for this server version, please read our resource page carefully.");
}
}
ProtocolRegistry.refreshVersions();
@@ -129,7 +152,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
@Override
public void onDisable() {
getLogger().info("ViaVersion is disabling, if this is a reload it may not work.");
getLogger().info("ViaVersion is disabling, if this is a reload and you experience issues consider rebooting.");
uninject();
}
@@ -200,7 +223,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
try {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
return;
}
for (Field field : connection.getClass().getDeclaredFields()) {
@@ -234,11 +257,32 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
}
System.setProperty("ViaVersion", getDescription().getVersion());
} catch (Exception e) {
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
getLogger().severe("Unable to inject ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version.");
e.printStackTrace();
}
}
public void patchLists() throws Exception {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
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();
@@ -282,12 +326,12 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
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());
throw new Exception("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
}
} catch (Exception e) {
getLogger().severe("Have you got late-bind enabled with something else?");
getLogger().severe("We failed to inject ViaVersion, have you got late-bind enabled with something else?");
e.printStackTrace();
}
}
@@ -302,7 +346,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((ViaVersionInitializer) oldInit).getOriginal());
}
} catch (Exception e) {
System.out.println("Failed to remove injection... reload won't work with connections sorry");
System.out.println("Failed to remove injection handler, reload won't work with connections, please reboot!");
}
}
injectedFutures.clear();
@@ -314,7 +358,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
pair.getKey().set(pair.getValue(), ((ListWrapper) o).getOriginalList());
}
} catch (IllegalAccessException e) {
System.out.println("Failed to remove injection... reload might not work with connections sorry");
System.out.println("Failed to remove injection, reload won't work with connections, please reboot!");
}
}
@@ -329,17 +373,25 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
@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);
@@ -400,7 +452,10 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
@Override
public SortedSet<Integer> getSupportedVersions() {
return ProtocolRegistry.getSupportedVersions();
SortedSet<Integer> outputSet = new TreeSet<>(ProtocolRegistry.getSupportedVersions());
outputSet.removeAll(getConf().getBlockedProtocols());
return outputSet;
}
@Override
@@ -435,6 +490,10 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
}
}
public boolean isProtocolSupport() {
return protocolSupport;
}
public Map<UUID, UserConnection> getPortedPlayers() {
return portedPlayers;
}
@@ -443,7 +502,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
// Max PPS Checker
if (conf.getMaxPPS() > 0) {
if (info.getPacketsPerSecond() >= conf.getMaxPPS()) {
info.disconnect(conf.getMaxPPSKickMessage());
info.disconnect(conf.getMaxPPSKickMessage().replace("%pps", ((Long) info.getPacketsPerSecond()).intValue() + ""));
return true; // don't send current packet
}
}
@@ -461,7 +520,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
}
if (info.getWarnings() >= conf.getMaxWarnings()) {
info.disconnect(conf.getMaxWarningsKickMessage());
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;
@@ -78,6 +84,27 @@ 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
*
@@ -197,6 +224,7 @@ public class PacketWrapper {
}
if (readableObjects.size() > 0) {
packetValues.addAll(readableObjects);
readableObjects.clear();
}
int index = 0;
@@ -241,7 +269,7 @@ public class PacketWrapper {
private void writeRemaining(ByteBuf output) {
if (inputBuffer != null) {
output.writeBytes(inputBuffer);
output.writeBytes(inputBuffer, inputBuffer.readableBytes());
}
}
@@ -250,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);
@@ -284,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
*/
@@ -313,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();
}
@@ -0,0 +1,75 @@
package us.myles.ViaVersion.api;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import java.util.UUID;
@Getter(AccessLevel.PROTECTED)
@RequiredArgsConstructor
public abstract class ViaListener implements Listener {
private final ViaVersionPlugin plugin;
private final Class<? extends Protocol> requiredPipeline;
private boolean registered = false;
/**
* Get the UserConnection from a player
*
* @param player Player object
* @return The UserConnection
*/
protected UserConnection getUserConnection(@NonNull Player player) {
return getUserConnection(player.getUniqueId());
}
/**
* Get the UserConnection from an UUID
*
* @param uuid UUID object
* @return The UserConnection
*/
protected UserConnection getUserConnection(@NonNull UUID uuid) {
if (!plugin.isPorted(uuid)) return null;
return plugin.getConnection(uuid);
}
/**
* Checks if the player is on the selected pipe
*
* @param player Player Object
* @return True if on pipe
*/
protected boolean isOnPipe(Player player) {
return isOnPipe(player.getUniqueId());
}
/**
* Checks if the UUID is on the selected pipe
*
* @param uuid UUID Object
* @return True if on pipe
*/
protected boolean isOnPipe(UUID uuid) {
UserConnection userConnection = getUserConnection(uuid);
return userConnection != null &&
userConnection.get(ProtocolInfo.class).getPipeline().contains(requiredPipeline);
}
/**
* Register as Bukkit event
*/
public void register() {
if (registered) return;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
registered = true;
}
}
@@ -6,6 +6,7 @@ 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;
@@ -16,11 +17,13 @@ public interface ViaVersionAPI {
*
* @param player Bukkit player object
* @return True if the client is modified (At the moment it also means version 1.9 and higher)
* @deprecated As of 0.9.9, because all players are ported use {@link #getPlayerVersion(Player)}
*/
boolean isPorted(Player player);
/**
* 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)
@@ -40,7 +43,9 @@ public interface ViaVersionAPI {
*
* @param playerUUID UUID of a player
* @return True if the client is on 1.9
* @deprecated As of 0.9.9, because all players are ported use {@link #getPlayerVersion(UUID)}
*/
@Deprecated
boolean isPorted(UUID playerUUID);
/**
@@ -113,16 +118,26 @@ public interface ViaVersionAPI {
/**
* Get the supported protocol versions
* This method removes any blocked protocol versions.
*
* @return a list of protocol versions
* @see ProtocolRegistry#getSupportedVersions() for full list.
*/
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,5 +1,7 @@
package us.myles.ViaVersion.api;
import java.util.List;
public interface ViaVersionConfig {
/**
@@ -172,4 +174,47 @@ public interface ViaVersionConfig {
* @return if true, enabled
*/
boolean isNMSPlayerTicking();
/**
* Replace extended pistons on 1.10 chunk loading.
*
* @return True if to replace them
*/
boolean isReplacePistons();
/**
* Get the id for replacing extended pistons.
*
* @return The integer id
*/
int getPistonReplacementId();
/**
* Force json transform
*
* @return True if enabled
*/
boolean isForceJsonTransform();
/**
* Get the blocked protocols
*
* @return An Integer list
*/
List<Integer> getBlockedProtocols();
/**
* Get the custom disconnect message
*
* @return Disconnect message
*/
String getBlockedDisconnectMsg();
/**
* Get the message sent to players being kicked on reload.
* Players are kicked to stop the server crashing
*
* @return Disconnect message
*/
String getReloadDisconnectMsg();
}
@@ -59,7 +59,7 @@ public abstract class ViaSubCommand {
public List<String> onTabComplete(CommandSender sender, String[] args) {
return Collections.emptyList();
}
public String color(String s) {
return ViaCommandHandler.color(s);
}
@@ -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;
@@ -85,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)
*
@@ -1,38 +1,23 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
@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;
import java.util.List;
/**
* 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;
}
public interface Chunk {
int getX();
/**
* Does this chunk have biome data
*
* @return True if the chunk has biome data
*/
public boolean hasBiomeData() {
return biomeData != null && groundUp;
}
int getZ();
ChunkSection[] getSections();
boolean isGroundUp();
boolean isBiomeData();
byte[] getBiomeData();
int getBitmask();
List<CompoundTag> getBlockEntities();
}
@@ -1,191 +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
* @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;
}
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();
@@ -79,6 +79,11 @@ public class ProtocolRegistry {
}
}
/**
* 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));
}
@@ -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;
@@ -17,11 +27,21 @@ public class ProtocolVersion {
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"));
@@ -30,10 +50,12 @@ public class ProtocolVersion {
register(v1_9_2 = new ProtocolVersion(109, "1.9.2"));
register(v1_9_3 = new ProtocolVersion(110, "1.9.3/4"));
register(v1_10 = new ProtocolVersion(210, "1.10"));
register(unknown = new ProtocolVersion(-1, "UNKNOWN"));
}
public static void register(@NonNull ProtocolVersion protocol) {
versions.put(protocol.getId(), protocol);
versionList.add(protocol);
}
public static boolean isRegistered(int id) {
@@ -48,7 +70,30 @@ public class ProtocolVersion {
}
}
public static int getIndex(ProtocolVersion version) {
return versionList.indexOf(version);
}
public static List<ProtocolVersion> getProtocols() {
return Collections.unmodifiableList(new ArrayList<>(versions.values()));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProtocolVersion that = (ProtocolVersion) o;
return id == that.id;
}
@Override
public int hashCode() {
return id;
}
@Override
public String toString() {
return String.format("%s(%d)", this.getName(), this.getId());
}
}
@@ -10,8 +10,6 @@ import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.type.types.*;
import us.myles.ViaVersion.api.type.types.minecraft.*;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@Getter
@@ -61,6 +59,7 @@ 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();
@@ -70,7 +69,6 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
/* Actual Class */
private final Class<? super T> outputClass;
private final Set<Class<? extends Type>> compatibilities;
private final String typeName;
public Type(Class<? super T> outputClass) {
@@ -80,7 +78,6 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
public Type(String typeName, Class<? super T> outputClass) {
this.outputClass = outputClass;
this.typeName = typeName;
this.compatibilities = new HashSet<>();
}
public Class<? extends Type> getBaseClass() {
@@ -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;
}
}
@@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.types;
package us.myles.ViaVersion.api.type.types.version;
import io.netty.buffer.ByteBuf;
@@ -6,7 +6,7 @@ import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.types.minecraft.MetaTypeTemplate;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.MetadataTypes;
public class MetadataType extends MetaTypeTemplate {
public class Metadata1_8Type extends MetaTypeTemplate {
@Override
public Metadata read(ByteBuf buffer) throws Exception {
@@ -19,13 +19,9 @@ public class MetadataType extends MetaTypeTemplate {
}
@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());
}
public void write(ByteBuf buffer, Metadata meta) throws Exception {
byte item = (byte) (meta.getTypeID() << 5 | meta.getId() & 0x1F);
buffer.writeByte(item);
meta.getType().write(buffer, meta.getValue());
}
}
@@ -1,11 +1,11 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types;
package us.myles.ViaVersion.api.type.types.version;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.types.minecraft.MetaTypeTemplate;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.NewType;
public class Meta1_10Type extends MetaTypeTemplate {
public class Metadata1_9Type extends MetaTypeTemplate {
@Override
public Metadata read(ByteBuf buffer) throws Exception {
@@ -1,21 +1,20 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.types;
package us.myles.ViaVersion.api.type.types.version;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.types.minecraft.MetaListTypeTemplate;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import java.util.ArrayList;
import java.util.List;
public class MetadataListType extends MetaListTypeTemplate {
public class MetadataList1_8Type extends MetaListTypeTemplate {
@Override
public List<Metadata> read(ByteBuf buffer) throws Exception {
List<Metadata> list = new ArrayList<>();
Metadata m;
do {
m = Protocol1_9TO1_8.METADATA.read(buffer);
m = Types1_8.METADATA.read(buffer);
if (m != null) {
list.add(m);
}
@@ -26,10 +25,8 @@ public class MetadataListType extends MetaListTypeTemplate {
@Override
public void write(ByteBuf buffer, List<Metadata> object) throws Exception {
for (Metadata m : object) {
Protocol1_9TO1_8.METADATA.write(buffer, m);
}
// Write end of list
Protocol1_9TO1_8.METADATA.write(buffer, null);
for (Metadata data : object)
Types1_8.METADATA.write(buffer, data);
buffer.writeByte(127);
}
}
@@ -0,0 +1,33 @@
package us.myles.ViaVersion.api.type.types.version;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.types.minecraft.MetaListTypeTemplate;
import java.util.ArrayList;
import java.util.List;
public class MetadataList1_9Type extends MetaListTypeTemplate {
@Override
public List<Metadata> read(ByteBuf buffer) throws Exception {
List<Metadata> list = new ArrayList<>();
Metadata meta;
do {
meta = Types1_9.METADATA.read(buffer);
if (meta != null)
list.add(meta);
} while (meta != null);
return list;
}
@Override
public void write(ByteBuf buffer, List<Metadata> object) throws Exception {
for (Metadata m : object)
Types1_9.METADATA.write(buffer, m);
// Write end of list
Types1_9.METADATA.write(buffer, null);
}
}
@@ -0,0 +1,18 @@
package us.myles.ViaVersion.api.type.types.version;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
public class Types1_8 {
/**
* Metadata list type for 1.8
*/
public static Type<List<Metadata>> METADATA_LIST = new MetadataList1_8Type();
/**
* Metadata type for 1.8
*/
public static Type<Metadata> METADATA = new Metadata1_8Type();
}
@@ -0,0 +1,18 @@
package us.myles.ViaVersion.api.type.types.version;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
public class Types1_9 {
/**
* Metadata list type for 1.9
*/
public static final Type<List<Metadata>> METADATA_LIST = new MetadataList1_9Type();
/**
* Metadata type for 1.9
*/
public static final Type<Metadata> METADATA = new Metadata1_9Type();
}
@@ -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);
}
@@ -132,6 +132,7 @@ public class ViaCommandHandler implements ViaVersionCommand, CommandExecutor, Ta
registerSubCommand(new ListSubCmd());
registerSubCommand(new PPSSubCmd());
registerSubCommand(new DebugSubCmd());
registerSubCommand(new DumpSubCmd());
registerSubCommand(new DisplayLeaksSubCmd());
registerSubCommand(new DontBugMeSubCmd());
registerSubCommand(new AutoTeamSubCmd());
@@ -0,0 +1,94 @@
package us.myles.ViaVersion.commands.defaultsubs;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.dump.DumpTemplate;
import us.myles.ViaVersion.dump.PluginInfo;
import us.myles.ViaVersion.dump.VersionInfo;
import java.io.InputStreamReader;
import java.io.InvalidObjectException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
public class DumpSubCmd extends ViaSubCommand {
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
@Override
public String name() {
return "dump";
}
@Override
public String description() {
return "Dump information about your server, this is helpful if you report bugs.";
}
@Override
public boolean execute(final CommandSender sender, String[] args) {
VersionInfo version = new VersionInfo(
Bukkit.getServer().getVersion(),
Bukkit.getServer().getBukkitVersion(),
System.getProperty("java.version"),
System.getProperty("os.name"),
ProtocolRegistry.SERVER_PROTOCOL,
ProtocolRegistry.getSupportedVersions());
List<PluginInfo> plugins = new ArrayList<>();
for (Plugin p : Bukkit.getPluginManager().getPlugins())
plugins.add(new PluginInfo(p.isEnabled(), p.getDescription().getName(), p.getDescription().getVersion(), p.getDescription().getMain(), p.getDescription().getAuthors()));
Map<String, Object> configuration = ((ViaVersionPlugin) ViaVersion.getInstance()).getConfig().getValues(false);
final DumpTemplate template = new DumpTemplate(version, configuration, plugins);
Bukkit.getScheduler().runTaskAsynchronously((ViaVersionPlugin) ViaVersion.getInstance(), new Runnable() {
@Override
public void run() {
try {
HttpURLConnection con = (HttpURLConnection) new URL("http://hastebin.com/documents").openConnection();
con.setRequestProperty("Content-Type", "text/plain");
con.setRequestMethod("POST");
con.setDoOutput(true);
OutputStream out = con.getOutputStream();
out.write(gson.toJson(template).getBytes(Charset.forName("UTF-8")));
out.close();
JsonObject output = gson.fromJson(new InputStreamReader(con.getInputStream()), JsonObject.class);
con.getInputStream().close();
if (!output.has("key"))
throw new InvalidObjectException("Key is not given in Hastebin output");
sender.sendMessage(ChatColor.GREEN + "We've made a dump with useful information, report your issue and provide this url: " + getUrl(output.get("key").getAsString()));
} catch (Exception e) {
sender.sendMessage(ChatColor.RED + "Failed to dump, please check the console for more information");
((ViaVersionPlugin) ViaVersion.getInstance()).getLogger().log(Level.WARNING, "Could not paste ViaVersion dump to Hastebin", e);
}
}
});
return true;
}
private String getUrl(String id) {
return String.format("http://hastebin.com/%s.json", id);
}
}
@@ -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();
@@ -0,0 +1,15 @@
package us.myles.ViaVersion.dump;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
@AllArgsConstructor
public class DumpTemplate {
private VersionInfo versionInfo;
private Map<String, Object> configuration;
private List<PluginInfo> plugins;
}
@@ -0,0 +1,16 @@
package us.myles.ViaVersion.dump;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class PluginInfo {
private boolean enabled;
private String name;
private String version;
private String main;
private List<String> authors;
}
@@ -0,0 +1,18 @@
package us.myles.ViaVersion.dump;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Set;
@Data
@AllArgsConstructor
public class VersionInfo {
private String version;
private String bukkitVersion;
private String javaVersion;
private String operatingSystem;
private int serverProtocol;
private Set<Integer> enabledPipelines;
}
@@ -38,7 +38,7 @@ public class ViaDecodeHandler extends ByteToMessageDecoder {
// Increment received
boolean second = info.incrementReceived();
// Check PPS
if(second) {
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,20 +50,26 @@ public class BaseProtocol extends Protocol {
try {
JSONObject json = (JSONObject) new JSONParser().parse(originalStatus);
JSONObject version = (JSONObject) json.get("version");
if (ViaVersion.getConfig().isSendSupportedVersions())
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) {
Long original = (Long) version.get("protocol");
ProtocolRegistry.SERVER_PROTOCOL = original.intValue();
}
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();
@@ -87,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}",
@@ -129,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
@@ -150,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
}
@@ -170,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);
}
}
}
}
@@ -8,21 +8,36 @@ 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.api.type.types.version.Metadata1_9Type;
import us.myles.ViaVersion.api.type.types.version.MetadataList1_9Type;
import us.myles.ViaVersion.api.type.types.version.Types1_9;
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.MetaList1_10Type;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types.Meta1_10Type;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
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();
@Deprecated
public static final Type<List<Metadata>> METADATA_LIST = new MetadataList1_9Type();
@Deprecated
public static final Type<Metadata> METADATA = new Metadata1_9Type();
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;
}
};
public static ValueTransformer<List<Metadata>, List<Metadata>> transformMetadata = new ValueTransformer<List<Metadata>, List<Metadata>>(Types1_9.METADATA_LIST) {
@Override
public List<Metadata> transform(PacketWrapper wrapper, List<Metadata> inputValue) throws Exception {
List<Metadata> metaList = new CopyOnWriteArrayList<>(inputValue);
for (Metadata m : metaList) {
if (m.getId() >= 5)
m.setId(m.getId() + 1);
}
return metaList;
}
};
@@ -69,7 +84,7 @@ public class Protocol1_10To1_9_3_4 extends Protocol {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity ID
map(METADATA_LIST); // 1 - Metadata list
map(Types1_9.METADATA_LIST, transformMetadata); // 1 - Metadata list
}
});
@@ -89,7 +104,7 @@ public class Protocol1_10To1_9_3_4 extends Protocol {
map(Type.SHORT); // 9 - Velocity X
map(Type.SHORT); // 10 - Velocity Y
map(Type.SHORT); // 11 - Velocity Z
map(METADATA_LIST); // 12 - Metadata
map(Types1_9.METADATA_LIST, transformMetadata); // 12 - Metadata
}
});
@@ -104,7 +119,7 @@ public class Protocol1_10To1_9_3_4 extends Protocol {
map(Type.DOUBLE); // 4 - Z
map(Type.BYTE); // 5 - Yaw
map(Type.BYTE); // 6 - Pitch
map(METADATA_LIST); // 7 - Metadata list
map(Types1_9.METADATA_LIST, transformMetadata); // 7 - Metadata list
}
});
@@ -1,41 +0,0 @@
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_4;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import java.util.List;
@Data
@AllArgsConstructor
public class Chunk1_9_3_4 {
private int x;
private int z;
private boolean groundUp;
private int bitmask;
private byte[] sections;
List<CompoundTag> blockEntities;
}
@@ -1,48 +0,0 @@
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.type.Type;
import java.util.ArrayList;
import java.util.List;
public class Chunk1_9_3_4Type extends Type<Chunk1_9_3_4> {
public Chunk1_9_3_4Type() {
super("1.9.3 Chunk", Chunk1_9_3_4.class);
}
@Override
public Chunk1_9_3_4 read(ByteBuf input) throws Exception {
int chunkX = input.readInt();
int chunkZ = input.readInt();
boolean groundUp = input.readBoolean();
int primaryBitmask = Type.VAR_INT.read(input);
int size = Type.VAR_INT.read(input);
byte[] sections = new byte[size];
input.readBytes(sections);
int blockEntities = Type.VAR_INT.read(input);
List<CompoundTag> nbtData = new ArrayList<>();
for (int i = 0; i < blockEntities; i++) {
nbtData.add(Type.NBT.read(input));
}
return new Chunk1_9_3_4(chunkX, chunkZ, groundUp, primaryBitmask, sections, nbtData);
}
@Override
public void write(ByteBuf buffer, Chunk1_9_3_4 chunk) throws Exception {
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
buffer.writeBoolean(chunk.isGroundUp());
Type.VAR_INT.write(buffer, chunk.getBitmask());
Type.VAR_INT.write(buffer, chunk.getSections().length);
buffer.writeBytes(chunk.getSections());
// no block entities as it's earlier
}
}
@@ -4,14 +4,17 @@ 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;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks.BlockEntity;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.types.Chunk1_9_3_4Type;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
public class Protocol1_9_1_2TO1_9_3_4 extends Protocol {
public static Type<Chunk1_9_3_4> CHUNK = new Chunk1_9_3_4Type();
@Override
protected void registerPackets() {
@@ -56,16 +59,59 @@ public class Protocol1_9_1_2TO1_9_3_4 extends Protocol {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Chunk1_9_3_4 chunk = wrapper.passthrough(CHUNK);
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
Chunk1_9_3_4Type type = new Chunk1_9_3_4Type(clientWorld);
Chunk chunk = wrapper.passthrough(type);
BlockEntity.handle(chunk.getBlockEntities(), wrapper.user());
}
});
}
});
// 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);
}
});
}
});
}
@Override
public void init(UserConnection userConnection) {
if (!userConnection.has(ClientWorld.class))
userConnection.put(new ClientWorld(userConnection));
}
}
@@ -1,4 +1,4 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4;
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks;
import lombok.Getter;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
@@ -7,6 +7,7 @@ import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.Protocol1_9_1_2TO1_9_3_4;
import java.util.List;
import java.util.Map;
@@ -26,9 +27,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 +43,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 +63,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,25 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks;
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;
private byte[] biomeData;
private List<CompoundTag> blockEntities;
@Override
public boolean isBiomeData() {
return biomeData != null;
}
}
@@ -0,0 +1,293 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks;
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_3_4 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_3_4() {
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,102 @@
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.types;
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.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
import us.myles.ViaVersion.api.type.PartialType;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks.Chunk1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks.ChunkSection1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
public Chunk1_9_3_4Type(ClientWorld param) {
super(param, Chunk.class);
}
@Override
public Chunk read(ByteBuf input, ClientWorld world) throws Exception {
int chunkX = input.readInt();
int chunkZ = input.readInt();
boolean groundUp = input.readBoolean();
int primaryBitmask = Type.VAR_INT.read(input);
Type.VAR_INT.read(input);
BitSet usedSections = new BitSet(16);
ChunkSection1_9_3_4[] sections = new ChunkSection1_9_3_4[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_3_4 section = new ChunkSection1_9_3_4();
sections[i] = section;
section.readBlocks(input);
section.readBlockLight(input);
if (world.getEnvironment() == World.Environment.NORMAL) {
section.readSkyLight(input);
}
}
byte[] biomeData = groundUp ? new byte[256] : null;
if (groundUp) {
input.readBytes(biomeData);
}
List<CompoundTag> nbtData = Arrays.asList(Type.NBT_ARRAY.read(input));
return new Chunk1_9_3_4(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
}
@Override
public void write(ByteBuf output, ClientWorld world, Chunk chunk) throws Exception {
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());
}
// Don't write block entities
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
}
@@ -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
@@ -6,11 +6,19 @@ 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.chunks.FakeTileEntity;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.types.Chunk1_9_1_2Type;
import java.util.ArrayList;
import java.util.List;
public class Protocol1_9_3TO1_9_1_2 extends Protocol {
@Override
@@ -24,7 +32,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 +68,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 +141,8 @@ public class Protocol1_9_3TO1_9_1_2 extends Protocol {
}
@Override
public void init(UserConnection userConnection) {
public void init(UserConnection user) {
if (!user.has(ClientWorld.class))
user.put(new ClientWorld(user));
}
}
@@ -0,0 +1,24 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.chunks;
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,293 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.chunks;
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.chunks;
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;
}
}
@@ -0,0 +1,33 @@
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:
return World.Environment.NETHER;
//throw new IllegalArgumentException("Invalid environment id:" + id);
}
}
}
@@ -0,0 +1,110 @@
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.types;
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.chunks.Chunk1_9_1_2;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.chunks.ChunkSection1_9_1_2;
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;
}
}
@@ -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;
}
@@ -13,12 +13,12 @@ import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.ValueTransformer;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.version.Metadata1_8Type;
import us.myles.ViaVersion.api.type.types.version.MetadataList1_8Type;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.packets.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.*;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.MetadataListType;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.MetadataType;
import java.util.List;
import java.util.UUID;
@@ -26,8 +26,11 @@ import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class Protocol1_9TO1_8 extends Protocol {
public static Type<List<Metadata>> METADATA_LIST = new MetadataListType();
public static Type<Metadata> METADATA = new MetadataType();
@Deprecated
public static Type<List<Metadata>> METADATA_LIST = new MetadataList1_8Type();
@Deprecated
public static Type<Metadata> METADATA = new Metadata1_8Type();
private static Gson gson = new GsonBuilder().create();
public static ValueTransformer<String, String> FIX_JSON = new ValueTransformer<String, String>(Type.STRING) {
@Override
@@ -41,9 +44,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 +53,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());
@@ -94,13 +105,15 @@ public class Protocol1_9TO1_8 extends Protocol {
@Override
protected void registerListeners() {
ViaVersionPlugin plugin = (ViaVersionPlugin) Bukkit.getPluginManager().getPlugin("ViaVersion");
Bukkit.getPluginManager().registerEvents(new ArmorListener(plugin), plugin);
Bukkit.getPluginManager().registerEvents(new CommandBlockListener(plugin), plugin);
Bukkit.getPluginManager().registerEvents(new DeathListener(plugin), plugin);
Bukkit.getPluginManager().registerEvents(new BlockListener(plugin), plugin);
if (Bukkit.getVersion().toLowerCase().contains("paper")) {
plugin.getLogger().info("Enabling PaperSpigot patch: Fixes block placement.");
Bukkit.getPluginManager().registerEvents(new PaperPatch(), plugin);
new ArmorListener(plugin).register();
new CommandBlockListener(plugin).register();
new DeathListener(plugin).register();
new BlockListener(plugin).register();
if (Bukkit.getVersion().toLowerCase().contains("paper") || Bukkit.getVersion().toLowerCase().contains("taco")) {
plugin.getLogger().info("Enabling PaperSpigot/TacoSpigot patch: Fixes block placement.");
new PaperPatch(plugin).register();
}
if (plugin.getConf().isStimulatePlayerTick())
new ViaIdleThread(plugin.getPortedPlayers()).runTaskTimer(plugin, 1L, 1L); // Updates player's idle status
@@ -0,0 +1,58 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import java.util.Collections;
import java.util.List;
@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;
}
@Override
public List<CompoundTag> getBlockEntities() {
return Collections.emptyList();
}
}
@@ -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,14 +1,10 @@
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;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
@@ -17,47 +13,42 @@ import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.CraftingInventory;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaListener;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.ArmorType;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import java.util.UUID;
@RequiredArgsConstructor
public class ArmorListener implements Listener {
public class ArmorListener extends ViaListener {
private static final UUID ARMOR_ATTRIBUTE = UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150");
private final ViaVersionPlugin plugin;
public static void sendArmorUpdate(Player player) {
public ArmorListener(ViaVersionPlugin plugin) {
super(plugin, Protocol1_9TO1_8.class);
}
public void sendArmorUpdate(Player player) {
// Ensure that the player is on our pipe
UserConnection userConnection = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(player);
if (userConnection == null) return;
if (!userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
if (!isOnPipe(player)) 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, getUserConnection(player));
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();
}
}
@@ -66,17 +57,15 @@ public class ArmorListener implements Listener {
HumanEntity human = e.getWhoClicked();
if (human instanceof Player && e.getInventory() instanceof CraftingInventory) {
final Player player = (Player) human;
if (ViaVersion.getInstance().isPorted(player)) {
if (e.getCurrentItem() != null) {
if (ArmorType.isArmor(e.getCurrentItem().getType())) {
sendDelayedArmorUpdate(player);
return;
}
}
if (e.getRawSlot() >= 5 && e.getRawSlot() <= 8) {
if (e.getCurrentItem() != null) {
if (ArmorType.isArmor(e.getCurrentItem().getType())) {
sendDelayedArmorUpdate(player);
return;
}
}
if (e.getRawSlot() >= 5 && e.getRawSlot() <= 8) {
sendDelayedArmorUpdate(player);
}
}
}
@@ -86,12 +75,10 @@ public class ArmorListener implements Listener {
if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) {
final Player player = e.getPlayer();
// Due to odd bugs it's 3 ticks later
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
Bukkit.getScheduler().scheduleSyncDelayedTask(getPlugin(), new Runnable() {
@Override
public void run() {
if (ViaVersion.getInstance().isPorted(player)) {
sendArmorUpdate(player);
}
sendArmorUpdate(player);
}
}, 3L);
}
@@ -105,23 +92,20 @@ public class ArmorListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onRespawn(PlayerRespawnEvent e) {
if (ViaVersion.getInstance().isPorted(e.getPlayer()))
sendDelayedArmorUpdate(e.getPlayer());
sendDelayedArmorUpdate(e.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldChange(PlayerChangedWorldEvent e) {
if (ViaVersion.getInstance().isPorted(e.getPlayer()))
sendArmorUpdate(e.getPlayer());
sendArmorUpdate(e.getPlayer());
}
public void sendDelayedArmorUpdate(final Player player) {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
if (!isOnPipe(player)) return; // Don't start a task if the player is not on the pipe
Bukkit.getScheduler().scheduleSyncDelayedTask(getPlugin(), new Runnable() {
@Override
public void run() {
if (ViaVersion.getInstance().isPorted(player)) {
sendArmorUpdate(player);
}
sendArmorUpdate(player);
}
});
}
@@ -1,30 +1,28 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
import lombok.RequiredArgsConstructor;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.ViaListener;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker;
@RequiredArgsConstructor
public class BlockListener implements Listener {
public class BlockListener extends ViaListener {
private final ViaVersionPlugin plugin;
public BlockListener(ViaVersionPlugin plugin) {
super(plugin, Protocol1_9TO1_8.class);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void placeBlock(BlockPlaceEvent e) {
if (plugin.isPorted(e.getPlayer())) {
UserConnection c = plugin.getConnection(e.getPlayer());
if (!c.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
if (isOnPipe(e.getPlayer())) {
Block b = e.getBlockPlaced();
plugin.getConnection(e.getPlayer()).get(EntityTracker.class).addBlockInteraction(new Position((long) b.getX(), (long) b.getY(), (long) b.getZ()));
getUserConnection(e.getPlayer())
.get(EntityTracker.class)
.addBlockInteraction(new Position((long) b.getX(), (long) b.getY(), (long) b.getZ()));
}
}
}
@@ -3,14 +3,12 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.CommandBlock;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerInteractEvent;
@@ -19,11 +17,11 @@ 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.ViaVersion;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaListener;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
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.util.ReflectionUtil;
@@ -31,10 +29,11 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import java.lang.reflect.Method;
@RequiredArgsConstructor
public class CommandBlockListener implements Listener {
public class CommandBlockListener extends ViaListener {
private final ViaVersionPlugin plugin;
public CommandBlockListener(ViaVersionPlugin plugin) {
super(plugin, Protocol1_9TO1_8.class);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onJoin(PlayerJoinEvent e) {
@@ -43,7 +42,9 @@ public class CommandBlockListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onRespawn(final PlayerRespawnEvent e) {
Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
if (!isOnPipe(e.getPlayer())) return;
Bukkit.getScheduler().runTaskLater(getPlugin(), new Runnable() {
@Override
public void run() {
sendOp(e.getPlayer());
@@ -58,12 +59,7 @@ public class CommandBlockListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEvent e) {
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && plugin.isPorted(e.getPlayer()) && e.getPlayer().isOp()) {
// Ensure that the player is on our pipe
UserConnection userConnection = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(e.getPlayer());
if (userConnection == null) return;
if (!userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && isOnPipe(e.getPlayer()) && e.getPlayer().isOp()) {
try {
sendCommandBlockPacket(e.getClickedBlock(), e.getPlayer());
} catch (Exception ex) {
@@ -73,19 +69,16 @@ public class CommandBlockListener implements Listener {
}
private void sendOp(Player p) {
if (p.isOp() && plugin.isPorted(p)) {
// Ensure that the player is on our pipe
UserConnection userConnection = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(p);
if (userConnection == null) return;
if (!userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
if (p.isOp() && isOnPipe(p)) {
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, getUserConnection(p)); // Entity status
wrapper.write(Type.INT, p.getEntityId()); // Entity ID
wrapper.write(Type.BYTE, (byte) 26); //Hardcoded op permission level
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -97,26 +90,30 @@ 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);
PacketWrapper wrapper = generatePacket(updatePacket, getUserConnection(player));
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 {
@@ -1,40 +1,28 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaListener;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
@RequiredArgsConstructor
public class DeathListener implements Listener {
private final ViaVersionPlugin plugin;
public class DeathListener extends ViaListener {
public DeathListener(ViaVersionPlugin plugin) {
super(plugin, Protocol1_9TO1_8.class);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onDeath(PlayerDeathEvent e) {
Player p = e.getEntity();
if (ViaVersion.getConfig().isShowNewDeathMessages() && checkGamerule(p.getWorld()) && e.getDeathMessage() != null && checkPipeline(p)) {
if (isOnPipe(p) && ViaVersion.getConfig().isShowNewDeathMessages() && checkGamerule(p.getWorld()) && e.getDeathMessage() != null)
sendPacket(p, e.getDeathMessage());
}
}
public boolean checkPipeline(Player p) {
UserConnection userConnection = plugin.getConnection(p);
return userConnection != null && userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class);
}
private UserConnection getUserConnection(Player p) {
return plugin.getConnection(p);
}
public boolean checkGamerule(World w) {
@@ -46,19 +34,19 @@ public class DeathListener implements Listener {
}
private void sendPacket(final Player p, final String msg) {
Bukkit.getScheduler().runTask(plugin, new Runnable() {
Bukkit.getScheduler().runTask(getPlugin(), new Runnable() {
@Override
public void run() {
PacketWrapper wrapper = new PacketWrapper(0x2C, null, getUserConnection(p));
try {
wrapper.write(Type.VAR_INT, 2);
wrapper.write(Type.VAR_INT, p.getEntityId());
wrapper.write(Type.INT, p.getEntityId());
Protocol1_9TO1_8.FIX_JSON.write(wrapper, msg);
wrapper.send();
wrapper.write(Type.VAR_INT, 2); // Event - Entity dead
wrapper.write(Type.VAR_INT, p.getEntityId()); // Player ID
wrapper.write(Type.INT, p.getEntityId()); // Entity ID
Protocol1_9TO1_8.FIX_JSON.write(wrapper, msg); // Message
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
wrapper.clearInputBuffer();
}
}
});
@@ -1,32 +1,35 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.listeners;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
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;
import us.myles.ViaVersion.api.ViaListener;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
public class PaperPatch implements Listener {
public class PaperPatch extends ViaListener {
public PaperPatch(ViaVersionPlugin plugin) {
super(plugin, Protocol1_9TO1_8.class);
}
/*
This patch is applied when Paper is detected.
I'm unsure of what causes this but essentially,
placing blocks where your standing works?
placing blocks where you're standing works?
If there is a better fix then we'll replace this.
*/
@EventHandler(ignoreCancelled = true)
public void onPlace(BlockPlaceEvent e) {
if (ViaVersion.getInstance().isPorted(e.getPlayer())) {
UserConnection userConnection = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(e.getPlayer());
if (userConnection == null) return;
if (!userConnection.get(ProtocolInfo.class).getPipeline().contains(Protocol1_9TO1_8.class)) return;
if (isOnPipe(e.getPlayer())) {
Location diff = e.getPlayer().getLocation().subtract(e.getBlock().getLocation().add(0.5D, 0, 0.5D));
Material block = e.getBlockPlaced().getType();
if (!block.isSolid()) {
return;
}
if (e.getPlayer().getLocation().getBlock().equals(e.getBlock())) {
e.setCancelled(true);
} else {
@@ -10,6 +10,8 @@ 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.api.type.types.version.Types1_8;
import us.myles.ViaVersion.api.type.types.version.Types1_9;
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;
@@ -57,7 +59,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;
}
@@ -175,11 +177,11 @@ public class EntityPackets {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity ID
map(Protocol1_9TO1_8.METADATA_LIST); // 1 - Metadata List
map(Types1_8.METADATA_LIST, Types1_9.METADATA_LIST); // 1 - Metadata List
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
List<Metadata> metadataList = wrapper.get(Types1_9.METADATA_LIST, 0);
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
if (tracker.getClientEntityTypes().containsKey(entityID)) {
@@ -196,7 +198,7 @@ public class EntityPackets {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
List<Metadata> metadataList = wrapper.get(Types1_9.METADATA_LIST, 0);
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.handleMetadata(entityID, metadataList);
@@ -207,7 +209,7 @@ public class EntityPackets {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
List<Metadata> metadataList = wrapper.get(Types1_9.METADATA_LIST, 0);
if (metadataList.size() == 0) {
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
@@ -11,9 +11,9 @@ import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.remapper.ValueCreator;
import us.myles.ViaVersion.api.remapper.ValueTransformer;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.types.version.Types1_8;
import us.myles.ViaVersion.api.type.types.version.Types1_9;
import us.myles.ViaVersion.packets.State;
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.ItemRewriter;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.MetadataRewriter;
@@ -107,12 +107,12 @@ public class SpawnPackets {
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(wrapper.user().get(ProtocolInfo.class).getPipeline().contains(Protocol1_10To1_9_3_4.class) ? 6 : 5, NewType.Slot.getTypeID(), Type.ITEM, item);
Metadata potion = new Metadata(5, NewType.Slot.getTypeID(), Type.ITEM, item);
meta.add(potion);
wrapper.write(Protocol1_9TO1_8.METADATA_LIST, meta);
wrapper.write(Types1_9.METADATA_LIST, meta);
}
});
metaPacket.send();
metaPacket.send(Protocol1_9TO1_8.class);
}
}
});
@@ -208,11 +208,11 @@ public class SpawnPackets {
map(Type.SHORT); // 10 - Velocity Y
map(Type.SHORT); // 11 - Velocity Z
map(Protocol1_9TO1_8.METADATA_LIST);
map(Types1_8.METADATA_LIST, Types1_9.METADATA_LIST);
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
List<Metadata> metadataList = wrapper.get(Types1_9.METADATA_LIST, 0);
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
if (tracker.getClientEntityTypes().containsKey(entityID)) {
@@ -227,7 +227,7 @@ public class SpawnPackets {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
List<Metadata> metadataList = wrapper.get(Types1_9.METADATA_LIST, 0);
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.handleMetadata(entityID, metadataList);
@@ -305,7 +305,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();
}
@@ -313,12 +313,12 @@ public class SpawnPackets {
}
});
map(Protocol1_9TO1_8.METADATA_LIST);
map(Types1_8.METADATA_LIST, Types1_9.METADATA_LIST);
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
List<Metadata> metadataList = wrapper.get(Types1_9.METADATA_LIST, 0);
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
if (tracker.getClientEntityTypes().containsKey(entityID)) {
@@ -334,7 +334,7 @@ public class SpawnPackets {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
List<Metadata> metadataList = wrapper.get(Protocol1_9TO1_8.METADATA_LIST, 0);
List<Metadata> metadataList = wrapper.get(Types1_9.METADATA_LIST, 0);
int entityID = wrapper.get(Type.VAR_INT, 0);
EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.handleMetadata(entityID, metadataList);
@@ -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();
// }
// });
}
});
@@ -35,7 +35,7 @@ public enum SoundEffect {
MOB_HORSE_ZOMBIE_DEATH("mob.horse.zombie.death", "entity.zombie_horse.death", SoundCategory.NEUTRAL),
MOB_SILVERFISH_KILL("mob.silverfish.kill", "entity.endermite.death", SoundCategory.HOSTILE),
MOB_WOLF_PANTING("mob.wolf.panting", "entity.wolf.pant", SoundCategory.NEUTRAL),
NOTE_BASS("note.bass", "null", SoundCategory.RECORD),
NOTE_BASS("note.bass", "block.note.bass", SoundCategory.RECORD),
DIG_STONE("dig.stone", "block.glass.place", SoundCategory.BLOCK),
MOB_ENDERMEN_STARE("mob.endermen.stare", "entity.endermen.stare", SoundCategory.HOSTILE),
GAME_PLAYER_SWIM_SPLASH("game.player.swim.splash", "entity.generic.splash", SoundCategory.BLOCK),
@@ -79,7 +79,7 @@ public enum SoundEffect {
CREEPER_PRIMED("creeper.primed", "entity.creeper.primed", SoundCategory.HOSTILE),
DIG_SAND("dig.sand", "block.sand.break", SoundCategory.BLOCK),
MOB_CHICKEN_SAY("mob.chicken.say", "entity.chicken.ambient", SoundCategory.NEUTRAL),
RANDOM_DOOR_CLOSE("random.door_close", "null", SoundCategory.BLOCK),
RANDOM_DOOR_CLOSE("random.door_close", "block.wooden_door.close", SoundCategory.BLOCK),
MOB_GUARDIAN_ELDER_DEATH("mob.guardian.elder.death", "entity.elder_guardian.death", SoundCategory.HOSTILE),
FIREWORKS_TWINKLE("fireworks.twinkle", "entity.firework.twinkle", SoundCategory.AMBIENT),
MOB_HORSE_SKELETON_DEATH("mob.horse.skeleton.death", "entity.skeleton_horse.death", SoundCategory.NEUTRAL),
@@ -177,7 +177,7 @@ public enum SoundEffect {
GAME_TNT_PRIMED("game.tnt.primed", "entity.creeper.primed", SoundCategory.BLOCK),
MOB_SHEEP_STEP("mob.sheep.step", "entity.sheep.step", SoundCategory.NEUTRAL),
MOB_ZOMBIE_DEATH("mob.zombie.death", "entity.zombie.death", SoundCategory.HOSTILE),
RANDOM_DOOR_OPEN("random.door_open", "null", SoundCategory.BLOCK),
RANDOM_DOOR_OPEN("random.door_open", "block.wooden_door.open", SoundCategory.BLOCK),
MOB_ENDERMEN_PORTAL("mob.endermen.portal", "entity.endermen.teleport", SoundCategory.HOSTILE),
MOB_HORSE_ANGRY("mob.horse.angry", "entity.horse.angry", SoundCategory.NEUTRAL),
MOB_WOLF_GROWL("mob.wolf.growl", "entity.wolf.growl", SoundCategory.NEUTRAL),
@@ -242,7 +242,7 @@ public enum SoundEffect {
MOB_RABBIT_IDLE("mob.rabbit.idle", "entity.rabbit.ambient", SoundCategory.NEUTRAL),
MUSIC_GAME_END("music.game.end", "music.end", SoundCategory.MUSIC),
MUSIC_GAME("music.game", "music.game", SoundCategory.MUSIC),
MOB_GUARDIAN_IDLE("mob.guardian.idle", "null", SoundCategory.HOSTILE),
MOB_GUARDIAN_IDLE("mob.guardian.idle", "entity.elder_guardian.ambient", SoundCategory.HOSTILE),
RECORDS_WARD("records.ward", "record.ward", SoundCategory.RECORD),
RECORDS_13("records.13", "record.13", SoundCategory.RECORD),
MOB_RABBIT_HOP("mob.rabbit.hop", "entity.rabbit.jump", 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);
}
}
@@ -81,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;
}
@@ -19,6 +19,7 @@ import us.myles.ViaVersion.api.minecraft.Position;
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.api.type.types.version.Types1_9;
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;
@@ -44,8 +45,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;
@@ -77,7 +76,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();
}
@@ -258,7 +257,7 @@ public class EntityTracker extends StoredObject {
}
teamExists = b;
try {
wrapper.send();
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
@@ -276,12 +275,12 @@ public class EntityTracker extends StoredObject {
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));
wrapper.write(Types1_9.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();
wrapper.send(Protocol1_9TO1_8.class);
} catch (Exception e) {
e.printStackTrace();
}
@@ -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());
}
}
}
@@ -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
@@ -21,8 +21,9 @@ import java.util.UUID;
public class UpdateUtil {
public final static String PREFIX = ChatColor.GREEN + "" + ChatColor.BOLD + "[ViaVersion] " + ChatColor.GREEN;
private final static String URL = "http://api.spiget.org/v1/resources/";
private final static String URL = "http://api.spiget.org/v2/resources/";
private final static int PLUGIN = 19254;
private final static String LATEST_VERSION = "/versions/latest";
public static void sendUpdateMessage(final UUID uuid, final Plugin plugin) {
new BukkitRunnable() {
@@ -81,20 +82,24 @@ public class UpdateUtil {
try {
current = new Version(ViaVersion.getInstance().getVersion());
} catch (IllegalArgumentException e) {
return "You are using a debug/custom version, consider updating.";
return "You are using a custom version, consider updating.";
}
Version newest = new Version(newestString);
if (current.compareTo(newest) < 0)
return "There is a newer version available: " + newest.toString();
return "There is a newer version available: " + newest.toString() + ", you're on: " + current.toString();
else if (console && current.compareTo(newest) != 0) {
return "You are running a newer version than is released!";
if (current.getTag().toLowerCase().startsWith("dev") || current.getTag().toLowerCase().startsWith("snapshot")) {
return "You are running a development version, please report any bugs to GitHub.";
} else {
return "You are running a newer version than is released!";
}
}
return null;
}
private static String getNewestVersion() {
try {
URL url = new URL(URL + PLUGIN + "?" + System.currentTimeMillis());
URL url = new URL(URL + PLUGIN + LATEST_VERSION + "?" + System.currentTimeMillis());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(true);
connection.addRequestProperty("User-Agent", "ViaVersion " + ViaVersion.getInstance().getVersion());
@@ -114,7 +119,7 @@ public class UpdateUtil {
e.printStackTrace();
return null;
}
return (String) statistics.get("version");
return (String) statistics.get("name");
} catch (MalformedURLException e) {
return null;
} catch (IOException e) {
@@ -2,21 +2,26 @@ package us.myles.ViaVersion.update;
import org.apache.commons.lang.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Version implements Comparable<Version> {
private int[] parts;
private static Pattern semVer = Pattern.compile("(?<a>0|[1-9]\\d*)\\.(?<b>0|[1-9]\\d*)(?:\\.(?<c>0|[1-9]\\d*))?(?:-(?<tag>[A-z0-9.-]*))?");
private int[] parts = new int[3];
private String tag;
public Version(String value) {
if (value == null)
throw new IllegalArgumentException("Version can not be null");
if (!value.matches("^[0-9]+(\\.[0-9]+)*$"))
Matcher matcher = semVer.matcher(value);
if (!matcher.matches())
throw new IllegalArgumentException("Invalid version format");
parts[0] = Integer.parseInt(matcher.group("a"));
parts[1] = Integer.parseInt(matcher.group("b"));
parts[2] = matcher.group("c") == null ? 0 : Integer.parseInt(matcher.group("c"));
String[] split = value.split("\\.");
parts = new int[split.length];
for (int i = 0; i < split.length; i += 1)
parts[i] = Integer.parseInt(split[i]);
tag = matcher.group("tag") == null ? "" : matcher.group("tag");
}
public static int compare(Version verA, Version verB) {
@@ -33,6 +38,12 @@ public class Version implements Comparable<Version> {
if (partA > partB) return 1;
}
// Simple tag check
if (verA.tag.length() == 0 && verB.tag.length() > 0)
return 1;
if (verA.tag.length() > 0 && verB.tag.length() == 0)
return -1;
return 0;
}
@@ -47,7 +58,7 @@ public class Version implements Comparable<Version> {
for (int i = 0; i < parts.length; i += 1)
split[i] = String.valueOf(parts[i]);
return StringUtils.join(split, ".");
return StringUtils.join(split, ".") + (tag.length() != 0 ? "-" + tag : "");
}
@Override
@@ -59,4 +70,8 @@ public class Version implements Comparable<Version> {
public boolean equals(Object that) {
return that instanceof Version && equals(this, (Version) that);
}
public String getTag() {
return tag;
}
}
@@ -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;
}
}
+25 -9
View File
@@ -12,28 +12,38 @@
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!"
# If you use ProtocolLib, we can't reload without kicking the players.
# (We don't suggest using reload either, use a plugin manager)
# You can customise the message we kick people with if you use ProtocolLib here.
reload-disconnect-msg: "Server reload, please rejoin!"
#
#----------------------------------------------------------#
# GLOBAL PACKET LIMITER #
#----------------------------------------------------------#
#
# Anti-Cheat, Packets Per Second (PPS) 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
#
# What is the maximum per second a client can send (Use %pps to display their pps)
# Use -1 to disable.
max-pps: 400
max-pps: 600
max-pps-kick-msg: "You are sending too many packets!"
#
# We can also kick them if over a period they send over a threshold a certain amount of times.
#
# Period to track (in seconds)
# Use -1 to disable.
tracking-period: 6
# How many packets per second counts as a warning
tracking-warning-pps: 120
# How many warnings over the interval can we have
# This can never be higher than "tracking-interval"
# This can never be higher than "tracking-period"
tracking-max-warnings: 4
tracking-max-kick-msg: "You are sending too many packets, :("
#
@@ -54,7 +64,7 @@ shield-blocking: true
# 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)
@@ -70,4 +80,10 @@ use-new-deathmessages: false
# Should we cache our items, this will prevent server from being lagged out, however the cost is a constant task caching items
item-cache: true
# Patch the Anti xray to work on 1.9 & 1.10 (If your server is 1.8) This can cost more performance, so disable it if you don't use it.
anti-xray-patch: true
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
+2 -1
View File
@@ -3,7 +3,8 @@ main: us.myles.ViaVersion.ViaVersionPlugin
author: _MylesC
version: ${project.version}
load: postworld
loadbefore: [ProtocolLib, ProxyPipe, SpigotLib, PacketListenerApi, SkinRestorer]
loadbefore: [ProtocolLib, ProxyPipe, SpigotLib, SkinRestorer]
softdepend: [ProtocolSupport, PacketListenerApi]
commands:
viaversion:
description: Shows ViaVersion Version and more.