Issues (29)

labymod/serverapi/bukkit/utils/PacketUtils.java (7 issues)

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
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
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.

See this CWE advisory on why this is an issue.

Loading history...
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
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
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
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
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
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
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
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
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
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
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