1 | package net.labymod.serverapi.bukkit.utils; |
||
2 | |||
3 | import io.netty.buffer.ByteBuf; |
||
4 | import io.netty.buffer.Unpooled; |
||
5 | import lombok.Getter; |
||
6 | import net.labymod.serverapi.bukkit.LabyModPlugin; |
||
7 | import org.bukkit.Bukkit; |
||
8 | import org.bukkit.entity.Player; |
||
9 | |||
10 | import java.lang.reflect.Constructor; |
||
11 | import java.lang.reflect.Field; |
||
12 | import java.lang.reflect.InvocationTargetException; |
||
13 | import java.lang.reflect.Method; |
||
14 | |||
15 | /** |
||
16 | * Class created by qlow | Jan |
||
17 | */ |
||
18 | public class PacketUtils { |
||
19 | |||
20 | private String version; |
||
21 | |||
22 | private Class<?> packetClass; |
||
23 | |||
24 | private Class<?> packetPlayOutCustomPayloadClass; |
||
25 | private Constructor<?> customPayloadConstructor; |
||
26 | private boolean customPayloadHasBytes; |
||
27 | |||
28 | private Class<?> packetDataSerializerClass; |
||
29 | private Constructor<?> packetDataSerializerConstructor; |
||
30 | |||
31 | private Method getHandleMethod; |
||
32 | private Field playerConnectionField; |
||
33 | @Getter |
||
34 | private Field networkManagerField; |
||
35 | |||
36 | public PacketUtils() { |
||
37 | this.version = Bukkit.getServer().getClass().getPackage().getName().replace( ".", "," ).split( "," )[3]; |
||
38 | |||
39 | try { |
||
40 | this.packetClass = getNmsClass( "Packet" ); |
||
41 | this.packetPlayOutCustomPayloadClass = getNmsClass( "PacketPlayOutCustomPayload" ); |
||
42 | this.networkManagerField = getNmsClass( "PlayerConnection" ).getDeclaredField( "networkManager" ); |
||
43 | } catch ( ClassNotFoundException | NoSuchFieldException e ) { |
||
44 | e.printStackTrace(); |
||
0 ignored issues
–
show
Best Practice
introduced
by
![]() |
|||
45 | } |
||
46 | |||
47 | if ( this.packetPlayOutCustomPayloadClass != null ) { |
||
48 | for ( Constructor<?> constructors : packetPlayOutCustomPayloadClass.getDeclaredConstructors() ) { |
||
49 | if ( constructors.getParameterTypes().length == 2 && constructors.getParameterTypes()[1] == byte[].class ) { |
||
50 | customPayloadHasBytes = true; |
||
51 | customPayloadConstructor = constructors; |
||
52 | } else if ( constructors.getParameterTypes().length == 2 && constructors.getParameterTypes()[1].getSimpleName().equals( "PacketDataSerializer" ) ) { |
||
0 ignored issues
–
show
Comparing classes by name is not safe, since class names must only be unique within a package. Consider using the
instanceOf() operator instead.
![]() |
|||
53 | customPayloadConstructor = constructors; |
||
54 | } |
||
55 | } |
||
56 | |||
57 | if ( !customPayloadHasBytes ) { |
||
58 | try { |
||
59 | packetDataSerializerClass = getNmsClass( "PacketDataSerializer" ); |
||
60 | packetDataSerializerConstructor = packetDataSerializerClass.getDeclaredConstructor( ByteBuf.class ); |
||
61 | } catch ( Exception ex ) { |
||
62 | ex.printStackTrace(); |
||
0 ignored issues
–
show
|
|||
63 | LabyModPlugin.getInstance().getLogger().severe( "Couldn't find a valid constructor for PacketPlayOutCustomPayload. Disabling the plugin." ); |
||
64 | Bukkit.getPluginManager().disablePlugin( LabyModPlugin.getInstance() ); |
||
65 | } |
||
66 | } |
||
67 | } |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * Gets the player's nms-handle |
||
72 | * |
||
73 | * @param player the bukkit-player |
||
74 | * @return the nms-handle |
||
75 | */ |
||
76 | public Object getPlayerHandle( Player player ) { |
||
77 | try { |
||
78 | if ( getHandleMethod == null ) |
||
79 | getHandleMethod = player.getClass().getMethod( "getHandle" ); |
||
80 | |||
81 | // Getting the player's nms-handle |
||
82 | return getHandleMethod.invoke( player ); |
||
83 | } catch ( Exception ex ) { |
||
84 | ex.printStackTrace(); |
||
0 ignored issues
–
show
|
|||
85 | } |
||
86 | |||
87 | return null; |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Gets the player's connection |
||
92 | * |
||
93 | * @param nmsPlayer the player's nms-handle |
||
94 | * @return the player-connection |
||
95 | */ |
||
96 | public Object getPlayerConnection( Object nmsPlayer ) { |
||
97 | try { |
||
98 | if ( playerConnectionField == null ) |
||
99 | playerConnectionField = nmsPlayer.getClass().getField( "playerConnection" ); |
||
100 | |||
101 | // Getting the player's connection |
||
102 | return playerConnectionField.get( nmsPlayer ); |
||
103 | } catch ( IllegalAccessException | NoSuchFieldException e ) { |
||
104 | e.printStackTrace(); |
||
0 ignored issues
–
show
|
|||
105 | } |
||
106 | |||
107 | return null; |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Sends a packet to the given player |
||
112 | * |
||
113 | * @param player the player the packet should be sent to |
||
114 | * @param packet the packet that should be sent to the player |
||
115 | */ |
||
116 | public void sendPacket( Player player, Object packet ) { |
||
117 | try { |
||
118 | // Getting the player's nms-handle |
||
119 | Object nmsPlayer = getPlayerHandle( player ); |
||
120 | |||
121 | // Getting the player's connection |
||
122 | Object playerConnection = getPlayerConnection( nmsPlayer ); |
||
123 | |||
124 | // Sending the packet |
||
125 | playerConnection.getClass().getMethod( "sendPacket", packetClass ).invoke( playerConnection, packet ); |
||
126 | } catch ( Exception ex ) { |
||
127 | ex.printStackTrace(); |
||
0 ignored issues
–
show
|
|||
128 | } |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Gets a constructed plugin message packet |
||
133 | * |
||
134 | * @param channel the channel-name |
||
135 | * @param bytes the bytes that should be sent with the packet |
||
136 | * @return a plugin-message packet |
||
137 | */ |
||
138 | public Object getPluginMessagePacket( String channel, byte[] bytes ) { |
||
139 | try { |
||
140 | return customPayloadConstructor.newInstance( channel, customPayloadHasBytes ? bytes : packetDataSerializerConstructor.newInstance( Unpooled.wrappedBuffer( bytes ) ) ); |
||
141 | } catch ( NullPointerException | InstantiationException | IllegalAccessException | InvocationTargetException e ) { |
||
142 | LabyModPlugin.getInstance().getLogger().severe( "Couldn't construct a custom-payload packet (Channel: " + channel + "):" ); |
||
143 | e.printStackTrace(); |
||
0 ignored issues
–
show
|
|||
144 | } |
||
145 | |||
146 | return null; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Gets a nms-class |
||
151 | * |
||
152 | * @param nmsClassName the nms-class name |
||
153 | * @return the multi-version compatible name of the class including the package |
||
154 | * @throws ClassNotFoundException if the class wasn't found |
||
155 | */ |
||
156 | public Class<?> getNmsClass( String nmsClassName ) throws ClassNotFoundException { |
||
157 | return Class.forName( "net.minecraft.server." + version + "." + nmsClassName ); |
||
158 | } |
||
159 | } |
||
160 |