├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── license-definitions.xml
├── pom.xml
└── src
├── main
└── java
│ └── net
│ └── beaconpe
│ └── jraklib
│ ├── Binary.java
│ ├── JRakLib.java
│ ├── Logger.java
│ ├── client
│ ├── ClientHandler.java
│ ├── ClientInstance.java
│ ├── Connection.java
│ ├── ConnectionManager.java
│ ├── JRakLibClient.java
│ └── UDPClientSocket.java
│ ├── protocol
│ ├── ACK.java
│ ├── ADVERTISE_SYSTEM.java
│ ├── AcknowledgePacket.java
│ ├── CLIENT_CONNECT_DataPacket.java
│ ├── CLIENT_DISCONNECT_DataPacket.java
│ ├── CLIENT_HANDSHAKE_DataPacket.java
│ ├── DataPacket.java
│ ├── DataPackets.java
│ ├── EncapsulatedPacket.java
│ ├── NACK.java
│ ├── OPEN_CONNECTION_REPLY_1.java
│ ├── OPEN_CONNECTION_REPLY_2.java
│ ├── OPEN_CONNECTION_REQUEST_1.java
│ ├── OPEN_CONNECTION_REQUEST_2.java
│ ├── PING_DataPacket.java
│ ├── PONG_DataPacket.java
│ ├── Packet.java
│ ├── SERVER_HANDSHAKE_DataPacket.java
│ ├── UNCONNECTED_PING.java
│ ├── UNCONNECTED_PING_OPEN_CONNECTIONS.java
│ └── UNCONNECTED_PONG.java
│ └── server
│ ├── JRakLibServer.java
│ ├── ServerHandler.java
│ ├── ServerInstance.java
│ ├── Session.java
│ ├── SessionManager.java
│ └── UDPServerSocket.java
└── test
└── java
└── TestClient.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk8
4 |
5 | sudo: false
6 |
7 | install: echo "No dependencies required :)"
8 |
9 | script: mvn test
10 |
11 | after_success: echo "Build Succeeded! :)"
12 | after_failure: echo "Build failed! :/"
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JRakLib [](https://travis-ci.org/BlockServerProject/JRakLib) [](https://tldrlegal.com/license/gnu-lesser-general-public-license-v3-(lgpl-3))
2 |
3 | #### NOTICE: A more improved version can be found at: https://github.com/jython234/JRakLibPlus
4 | A port of the PHP MCPE RakNet networking library: RakLib.
5 |
--------------------------------------------------------------------------------
/license-definitions.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 | /**EOL * JRakLib is not affiliated with Jenkins Software LLC or RakNet.EOL * This software is a port of RakLib https://github.com/PocketMine/RakLib.EOL
24 | *
25 | */
26 |
27 |
28 | (\s|\t)*/\*.*$
29 | .*\*/(\s|\t)*$
30 | false
31 | true
32 | false
33 |
34 |
35 |
36 |
37 | ]]>
38 |
39 | $]]>
40 |
41 | (\s|\t)*$]]>
42 | false
43 | true
44 | false
45 |
46 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
24 | 4.0.0
25 |
26 | net.beaconpe.jraklib
27 | JRakLib
28 | 1.1.1-SNAPSHOT
29 |
30 |
31 |
32 | snapshot-repo
33 | http://repo.blockserver.org/snapshots
34 |
35 |
36 |
37 |
38 |
39 |
40 | org.apache.maven.plugins
41 | maven-compiler-plugin
42 |
43 | 1.8
44 | 1.8
45 |
46 |
47 |
48 | com.mycila
49 | license-maven-plugin
50 | 2.11
51 |
52 | com/mycila/maven/plugin/license/templates/LGPL-3.txt
53 |
54 | license-definitions.xml
55 |
56 |
57 | **/README
58 | **/LICENSE
59 | src/test/resources/**
60 | src/main/resources/**
61 |
62 |
63 |
64 |
65 |
66 | check
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/Binary.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib;
21 |
22 |
23 | import java.nio.ByteBuffer;
24 | import java.util.ArrayList;
25 | import java.util.Arrays;
26 | import java.util.List;
27 |
28 | /**
29 | * Binary Utility class for writing/reading.
30 | */
31 | public class Binary {
32 |
33 | /**
34 | * Reads a 3-byte little-endian number
35 | * @param bytes
36 | * @return integer
37 | */
38 | public static int readLTriad(byte[] bytes){
39 | return (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8) | ((bytes[2] & 0x0F) << 16);
40 | }
41 |
42 | /**
43 | * Writes a 3-byte little-endian number
44 | * @param triad
45 | * @return triad bytes
46 | */
47 | public static byte[] writeLTriad(int triad){
48 | byte b1,b2,b3;
49 | b3 = (byte)(triad & 0xFF);
50 | b2 = (byte)((triad >> 8) & 0xFF);
51 | b1 = (byte)((triad >> 16) & 0xFF);
52 | return new byte[] {b3, b2, b1};
53 | }
54 |
55 | /**
56 | * Reads a signed byte as a boolean
57 | * @param b The raw byte
58 | * @return The boolean
59 | */
60 | public static boolean readBool(byte b){
61 | if(b == 0){
62 | return true;
63 | } else {
64 | return false;
65 | }
66 | }
67 |
68 | /**
69 | * Writes a signed boolean as a byte
70 | * @param b The boolean
71 | * @return Boolean as a byte
72 | */
73 | public static byte writeBool(boolean b){
74 | if(b){
75 | return 0x01;
76 | } else {
77 | return 0x00;
78 | }
79 | }
80 |
81 | /**
82 | * Reads a signed/unsigned byte
83 | * @param b The raw byte
84 | * @param signed If the byte is signed
85 | * @return Signed/unsigned byte as int.
86 | */
87 | public static int readByte(byte b, boolean signed){
88 | if(signed){
89 | return b;
90 | } else {
91 | return b & 0xFF;
92 | }
93 | }
94 |
95 | /**
96 | * Writes a signed/unsigned byte.
97 | * @param b Raw byte
98 | * @return The byte.
99 | */
100 | public static byte writeByte(byte b){
101 | return b;
102 | }
103 |
104 | /**
105 | * Reads an unsigned 16 bit big-endian number.
106 | * @param bytes Raw bytes
107 | * @return The unsigned short.
108 | */
109 | public static int readShort(byte[] bytes){
110 | return ((bytes[0] << 8) & 0x0000ff00) | (bytes[1] & 0x000000ff);
111 | }
112 |
113 | /**
114 | * Reads a signed 16 bit big-endian number.
115 | * @param bytes Raw bytes
116 | * @return The signed short.
117 | */
118 | public static short readSignedShort(byte[] bytes){
119 | return ByteBuffer.wrap(bytes).getShort();
120 | }
121 |
122 | /**
123 | * Writes a signed 16 bit big-endian number.
124 | * @param s The short
125 | * @return Short as a byte array
126 | */
127 | public static byte[] writeShort(short s){
128 | return ByteBuffer.allocate(2).putShort(s).array();
129 | }
130 |
131 | /**
132 | * Writes an unsigned 16 bit big-endian number.
133 | * @param s The unsigned short (integer)
134 | * @return Short as a byte array
135 | */
136 | public static byte[] writeUnsignedShort(int s){
137 | ByteBuffer bb = ByteBuffer.allocate(2);
138 | bb.put((byte) ((s >> 8) & 0xff));
139 | bb.put((byte) (s & 0xff));
140 | return bb.array();
141 | }
142 |
143 | /**
144 | * Reads a signed 32 bit big-endian number.
145 | * @param bytes Raw bytes
146 | * @return The integer.
147 | */
148 | public static int readInt(byte[] bytes){
149 | return ByteBuffer.wrap(bytes).getInt();
150 | }
151 |
152 | /**
153 | * Writes a signed 32 bit big-endian number.
154 | * @param i The integer.
155 | * @return Integer as a byte array
156 | */
157 | public static byte[] writeInt(int i){
158 | return ByteBuffer.allocate(4).putInt(i).array();
159 | }
160 |
161 | /**
162 | * Reads a signed 32 bit big-endian floating point number.
163 | * @param bytes Raw bytes
164 | * @return The float
165 | */
166 | public static float readFloat(byte[] bytes){
167 | return ByteBuffer.wrap(bytes).getFloat();
168 | }
169 |
170 | /**
171 | * Writes a signed 32 bit big-endian floating point number.
172 | * @param f The float.
173 | * @return The float as a byte array
174 | */
175 | public static byte[] writeFloat(float f){
176 | return ByteBuffer.allocate(4).putFloat(f).array();
177 | }
178 |
179 | /**
180 | * Reads a signed 64 bit big-endian double precision number.
181 | * @param bytes Raw bytes
182 | * @return The double.
183 | */
184 | public static double readDouble(byte[] bytes){
185 | return ByteBuffer.wrap(bytes).getFloat();
186 | }
187 |
188 | /**
189 | * Writes a signed 64 bit big-endian double precision number.
190 | * @param d The double.
191 | * @return The double as a byte array
192 | */
193 | public static byte[] writeDouble(double d){
194 | return ByteBuffer.allocate(8).putDouble(d).array();
195 | }
196 |
197 | /**
198 | * Reads a signed 64 bit big-endian number.
199 | * @param bytes Raw bytes
200 | * @return The long
201 | */
202 | public static long readLong(byte[] bytes){
203 | return ByteBuffer.wrap(bytes).getLong();
204 | }
205 |
206 | /**
207 | * Writes a signed 64 bit big-endian number.
208 | * @param l The long.
209 | * @return The long as a byte array.
210 | */
211 | public static byte[] writeLong(long l){
212 | return ByteBuffer.allocate(8).putLong(l).array();
213 | }
214 |
215 | public static byte[] subbytes(byte[] bytes, int start, int length){
216 | ByteBuffer bb = ByteBuffer.wrap(bytes);
217 | bb.position(start);
218 | byte[] bytes2 = new byte[length];
219 | bb.get(bytes2);
220 | return bytes2;
221 | }
222 |
223 | public static byte[] subbytes(byte[] bytes, int start){
224 | return subbytes(bytes, start, bytes.length - start);
225 | }
226 |
227 | public static byte[][] splitbytes(byte[] bytes, int chunkSize){
228 | byte[][] splits = new byte[1024][chunkSize];
229 | int chunks = 0;
230 | for(int i=0;i chunkSize){
232 | splits[chunks] = Arrays.copyOfRange(bytes, i, i + chunkSize);
233 | } else {
234 | splits[chunks] = Arrays.copyOfRange(bytes, i, bytes.length);
235 | }
236 | chunks++;
237 | }
238 |
239 | splits = Arrays.copyOf(splits, chunks);
240 |
241 | return splits;
242 | }
243 | }
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/JRakLib.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib;
21 |
22 | import java.time.Instant;
23 | import java.util.regex.Pattern;
24 |
25 | /**
26 | * JRakLib Constants Class.
27 | */
28 | public abstract class JRakLib {
29 | public static final String VERSION = "1.1.1-SNAPSHOT";
30 | public static final byte PROTOCOL = 7;
31 | public static final byte[] MAGIC = new byte[]{
32 | 0x00, (byte) 0xff, (byte) 0xff, 0x00,
33 | (byte) 0xfe, (byte) 0xfe, (byte) 0xfe, (byte) 0xfe,
34 | (byte) 0xfd, (byte) 0xfd, (byte) 0xfd, (byte) 0xfd,
35 | 0x12, 0x34, 0x56, 0x78 };
36 |
37 | public static final byte PRIORITY_NORMAL = 0;
38 | public static final byte PRIORITY_IMMEDIATE = 1;
39 |
40 | public static final byte FLAG_NEED_ACK = 0b00001000;
41 |
42 | /*
43 | * ENCAPSULATED payload:
44 | * byte (identifier length)
45 | * byte[] (identifier)
46 | * byte (flags, last 3 bits, priority)
47 | * payload (binary internal EncapsulatedPacket)
48 | */
49 | public static final byte PACKET_ENCAPSULATED = 0x01;
50 | /*
51 | * OPEN_SESSION payload:
52 | * byte (identifier length)
53 | * byte[] (identifier)
54 | * byte (address length)
55 | * byte[] (address)
56 | * short (port)
57 | * long (clientID)
58 | */
59 | public static final byte PACKET_OPEN_SESSION = 0x02;
60 | /*
61 | * CLOSE_SESSION payload:
62 | * byte (identifier length)
63 | * byte[] (identifier)
64 | * string (reason)
65 | */
66 | public static final byte PACKET_CLOSE_SESSION = 0x03;
67 | /*
68 | * INVALID_SESSION payload:
69 | * byte (identifier length)
70 | * byte[] (identifier)
71 | */
72 | public static final byte PACKET_INVALID_SESSION = 0x04;
73 | /* SEND_QUEUE payload:
74 | * byte (identifier length)
75 | * byte[] (identifier)
76 | */
77 | public static final byte PACKET_SEND_QUEUE = 0x05;
78 | /*
79 | * ACK_NOTIFICATION payload:
80 | * byte (identifier length)
81 | * byte[] (identifier)
82 | * int (identifierACK)
83 | */
84 | public static final byte PACKET_ACK_NOTIFICATION = 0x06;
85 | /*
86 | * SET_OPTION payload:
87 | * byte (option name length)
88 | * byte[] (option name)
89 | * byte[] (option value)
90 | */
91 | public static final byte PACKET_SET_OPTION = 0x07;
92 | /*
93 | * RAW payload:
94 | * byte (address length)
95 | * byte[] (address from/to)
96 | * short (port)
97 | * byte[] (payload)
98 | */
99 | public static final byte PACKET_RAW = 0x08;
100 | /*
101 | * RAW payload:
102 | * byte (address length)
103 | * byte[] (address)
104 | * int (timeout)
105 | */
106 | public static final byte PACKET_BLOCK_ADDRESS = 0x09;
107 | /*
108 | * No payload
109 | *
110 | * Sends the disconnect message, removes sessions correctly, closes sockets.
111 | */
112 | public static final byte PACKET_SHUTDOWN = 0x7e;
113 | /*
114 | * No payload
115 | *
116 | * Leaves everything as-is and halts, other Threads can be in a post-crash condition.
117 | */
118 | public static final byte PACKET_EMERGENCY_SHUTDOWN = 0x7f;
119 |
120 | /**
121 | * RAW payload:
122 | * byte (message length)
123 | * byte[] message
124 | * ushort (class length)
125 | * byte[] class message
126 | */
127 | public static final byte PACKET_EXCEPTION_CAUGHT = 0x7d;
128 |
129 | public static void sleepUntil(long time) {
130 | while (true) {
131 | if (Instant.now().toEpochMilli() >= time) {
132 | break;
133 | }
134 | }
135 | }
136 |
137 | public static String getAddressFromString(String address){
138 | if(address.contains("/")){
139 | return address.split(Pattern.quote("/"))[1].split(Pattern.quote(":"))[0];
140 | } else {
141 | return address.split(Pattern.quote(":"))[0];
142 | }
143 | }
144 |
145 | public static int getPortFromString(String address){
146 | if(address.contains("/")){
147 | return Integer.parseInt(address.split(Pattern.quote("/"))[1].split(Pattern.quote(":"))[1]);
148 | } else {
149 | return Integer.parseInt(address.split(Pattern.quote(":"))[1]);
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/Logger.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib;
21 |
22 | /**
23 | * Interface for an implementation of a logger wrapper that JRakLib can use.
24 | */
25 | public interface Logger {
26 | void notice(String message);
27 | void critical(String message);
28 | void emergency(String message);
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/client/ClientHandler.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.client;
21 |
22 | import net.beaconpe.jraklib.Binary;
23 | import net.beaconpe.jraklib.JRakLib;
24 | import net.beaconpe.jraklib.protocol.EncapsulatedPacket;
25 |
26 | import java.nio.ByteBuffer;
27 | import java.util.Arrays;
28 |
29 | /**
30 | * A handler class for handling the client.
31 | *
32 | * @author jython234
33 | */
34 | public class ClientHandler {
35 | protected JRakLibClient client;
36 | protected ClientInstance instance;
37 |
38 | public ClientHandler(JRakLibClient client, ClientInstance instance){
39 | this.client = client;
40 | this.instance = instance;
41 | }
42 |
43 | public void sendEncapsulated(EncapsulatedPacket packet){
44 | byte flags = JRakLib.PRIORITY_NORMAL;
45 | sendEncapsulated("", packet, flags);
46 | }
47 |
48 | public void sendEncapsulated(String identifier, EncapsulatedPacket packet, byte flags){
49 | ByteBuffer bb = ByteBuffer.allocate(3+identifier.getBytes().length+packet.getTotalLength(true));
50 | bb.put(JRakLib.PACKET_ENCAPSULATED).put((byte) identifier.getBytes().length).put(identifier.getBytes()).put(flags).put(packet.toBinary(true));
51 | client.pushMainToThreadPacket(Arrays.copyOf(bb.array(), bb.position()));
52 | bb = null;
53 | }
54 |
55 | public void sendRaw(byte[] payload){
56 | sendRaw(client.getServerIP(), (short) client.getServerPort(), payload);
57 | }
58 |
59 | public void sendRaw(String address, short port, byte[] payload){
60 | ByteBuffer bb = ByteBuffer.allocate(4+address.getBytes().length+payload.length);
61 | bb.put(JRakLib.PACKET_RAW).put((byte) address.getBytes().length).put(address.getBytes()).put(Binary.writeShort(port)).put(payload);
62 | client.pushMainToThreadPacket(bb.array());
63 | }
64 |
65 | public void sendOption(String name, String value){
66 | ByteBuffer bb = ByteBuffer.allocate(2+name.getBytes().length+value.getBytes().length);
67 | bb.put(JRakLib.PACKET_SET_OPTION).put((byte) name.getBytes().length).put(name.getBytes()).put(value.getBytes());
68 | client.pushMainToThreadPacket(bb.array());
69 | }
70 |
71 | public void disconnectFromServer(){
72 | shutdown();
73 | }
74 |
75 | public void shutdown(){
76 | client.shutdown();
77 | client.pushMainToThreadPacket(new byte[] {JRakLib.PACKET_SHUTDOWN});
78 | //TODO: Find a way to kill client after sleep.
79 | }
80 |
81 | public void emergencyShutdown(){
82 | client.shutdown();
83 | client.pushMainToThreadPacket(new byte[] {0x7f}); //JRakLib::PACKET_EMERGENCY_SHUTDOWN
84 | }
85 |
86 | public boolean handlePacket(){
87 | byte[] packet = client.readThreadToMainPacket();
88 | if(packet == null){
89 | return false;
90 | }
91 | if(packet.length > 0){
92 | byte id = packet[0];
93 | int offset = 1;
94 | if(id == JRakLib.PACKET_ENCAPSULATED){
95 | int len = packet[offset++];
96 | String identifier = new String(Binary.subbytes(packet, offset, len));
97 | offset += len;
98 | byte flags = packet[offset++];
99 | byte[] buffer = Binary.subbytes(packet, offset);
100 | instance.handleEncapsulated(EncapsulatedPacket.fromBinary(buffer, true), flags);
101 | } else if(id == JRakLib.PACKET_RAW){
102 | int len = packet[offset++];
103 | String address = new String(Binary.subbytes(packet, offset, len));
104 | offset += len;
105 | int port = Binary.readShort(Binary.subbytes(packet, offset, 2));
106 | offset += 2;
107 | byte[] payload = Binary.subbytes(packet, offset);
108 | instance.handleRaw(payload);
109 | } else if(id == JRakLib.PACKET_SET_OPTION){
110 | int len = packet[offset++];
111 | String name = new String(Binary.subbytes(packet, offset, len));
112 | offset += len;
113 | String value = new String(Binary.subbytes(packet, offset));
114 | instance.handleOption(name, value);
115 | } else if(id == JRakLib.PACKET_OPEN_SESSION){
116 | int len = packet[offset++];
117 | String identifier = new String(Binary.subbytes(packet, offset, len));
118 | offset += len;
119 | long serverID = Binary.readLong(Binary.subbytes(packet, offset, 8));
120 | instance.connectionOpened(serverID);
121 | } else if(id == JRakLib.PACKET_CLOSE_SESSION){
122 | int len = packet[offset++];
123 | String identifier = new String(Binary.subbytes(packet, offset, len));
124 | offset += len;
125 | len = packet[offset++];
126 | String reason = new String(Binary.subbytes(packet, offset, len));
127 | instance.connectionClosed(reason);
128 | }
129 | return true;
130 | }
131 | return false;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/client/ClientInstance.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.client;
21 |
22 | import net.beaconpe.jraklib.protocol.EncapsulatedPacket;
23 |
24 | /**
25 | * An interface for communication with the client implementation
26 | * .
27 | * @author jython234
28 | */
29 | public interface ClientInstance {
30 |
31 | /**
32 | * Called when the connection is opened to the RakNet server.
33 | * @param serverId The serverId of the RakNet server.
34 | */
35 | void connectionOpened(long serverId);
36 |
37 | /**
38 | * Called when the connection is closed.
39 | * @param reason The reason for the closure.
40 | */
41 | void connectionClosed(String reason);
42 |
43 | /**
44 | * Called when an EncapsulatedPacket
is received from the RakNet server.
45 | * @param packet The packet received.
46 | * @param flags Flags from the packet.
47 | */
48 | void handleEncapsulated(EncapsulatedPacket packet, int flags);
49 |
50 | /**
51 | * Called when an unrecognized packet is received from the server.
52 | * @param payload Raw payload (in bytes) of the strange packet.
53 | */
54 | void handleRaw(byte[] payload);
55 |
56 | /**
57 | * Called when an internal option is updated. Current only supports "bandwith"
58 | * @param option The option name.
59 | * @param value The option value.
60 | */
61 | void handleOption(String option, String value);
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/client/Connection.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.client;
21 |
22 | import net.beaconpe.jraklib.Binary;
23 | import net.beaconpe.jraklib.JRakLib;
24 | import net.beaconpe.jraklib.protocol.*;
25 |
26 | import java.io.IOException;
27 | import java.net.InetAddress;
28 | import java.net.InetSocketAddress;
29 | import java.nio.ByteBuffer;
30 | import java.time.Instant;
31 | import java.util.*;
32 | import java.util.concurrent.ConcurrentHashMap;
33 |
34 | /**
35 | * Represents a Connection to a server.
36 | *
37 | * @author jython234
38 | */
39 | public class Connection {
40 |
41 | public final static int STATE_UNCONNECTED = 0;
42 | public final static int STATE_CONNECTING_1 = 1;
43 | public final static int STATE_CONNECTING_2 = 2;
44 | public final static int STATE_CONNECTING_3 = 3;
45 | public final static int STATE_CONNECTED = 4;
46 |
47 | public static long sessionID = new Random().nextLong();
48 | public static int WINDOW_SIZE = 2048;
49 |
50 | protected ConnectionManager manager;
51 |
52 | private int state = STATE_UNCONNECTED;
53 | private List preJoinQueue = new ArrayList<>();
54 | private int mtuSize = 548; //Min size
55 | private long id = 0;
56 | private int splitID = 0;
57 |
58 | private int messageIndex = 0;
59 | private Map channelIndex = new ConcurrentHashMap<>();
60 |
61 | private int sendSeqNumber = 0;
62 | private int lastSeqNumber = -1;
63 |
64 | private long lastUpdate;
65 | private long startTime;
66 |
67 | private List packetToSend = new ArrayList<>();
68 |
69 | private boolean isActive;
70 |
71 | private List ACKQueue = new ArrayList<>();
72 | private List NACKQueue = new ArrayList<>();
73 |
74 | private Map recoveryQueue = new ConcurrentHashMap<>();
75 |
76 | private Map> splitPackets = new ConcurrentHashMap<>();
77 |
78 | private Map> needACK = new ConcurrentHashMap<>();
79 |
80 |
81 | private DataPacket sendQueue;
82 |
83 | private int windowStart;
84 | private Map receivedWindow = new ConcurrentHashMap<>();
85 | private int windowEnd;
86 |
87 | private int reliableWindowStart;
88 | private int reliableWindowEnd;
89 | private Map reliableWindow = new ConcurrentHashMap<>();
90 | private int lastReliableIndex = -1;
91 |
92 | private long lastPing;
93 | private long lastPong = Instant.now().toEpochMilli();
94 |
95 | public Connection(ConnectionManager manager, int mtuSize){
96 | this.manager = manager;
97 | this.mtuSize = mtuSize;
98 | sendQueue = new DataPackets.DATA_PACKET_4();
99 | lastUpdate = Instant.now().toEpochMilli();
100 | startTime = Instant.now().toEpochMilli();
101 | isActive = false;
102 | windowStart = -1;
103 | windowEnd = WINDOW_SIZE;
104 |
105 | reliableWindowStart = 0;
106 | reliableWindowEnd = WINDOW_SIZE;
107 |
108 | for(byte i = 0; i < 32; i++){
109 | channelIndex.put(i, 0);
110 | }
111 | }
112 |
113 | public void update(long time) throws IOException {
114 | isActive = false;
115 | if((Instant.now().toEpochMilli() - lastPong) >= 8000){
116 | disconnect("connection timed out.");
117 | }
118 | if((Instant.now().toEpochMilli() - lastPing) >= 5000){
119 | sendPing();
120 | }
121 | if(!ACKQueue.isEmpty()){
122 | ACK pk = new ACK();
123 | pk.packets = ACKQueue.stream().toArray(Integer[]::new);
124 | sendPacket(pk);
125 | ACKQueue.clear();
126 | }
127 |
128 | if(!NACKQueue.isEmpty()){
129 | NACK pk = new NACK();
130 | pk.packets = NACKQueue.stream().toArray(Integer[]::new);
131 | sendPacket(pk);
132 | NACKQueue.clear();
133 | }
134 |
135 | if(!packetToSend.isEmpty()){
136 | int limit = 16;
137 | for(int i = 0; i < packetToSend.size(); i++){
138 | DataPacket pk = packetToSend.get(i);
139 | pk.sendTime = time;
140 | pk.encode();
141 | recoveryQueue.put(pk.seqNumber, pk);
142 | packetToSend.remove(pk);
143 | sendPacket(pk);
144 | if(limit-- <= 0){
145 | break;
146 | }
147 | }
148 | }
149 |
150 | if(packetToSend.size() > WINDOW_SIZE){
151 | packetToSend.clear();
152 | }
153 |
154 | if(needACK.values().size() > 0){
155 | for(Integer i : needACK.keySet()){
156 | Map indexes = needACK.get(i);
157 | if(indexes.values().size() == 0){
158 | needACK.remove(indexes);
159 | //sessionManager.notifyACK(this, i);
160 | }
161 | }
162 | }
163 |
164 | for(Integer seq : recoveryQueue.keySet()){
165 | DataPacket pk = recoveryQueue.get(seq);
166 | if(pk.sendTime < Instant.now().toEpochMilli() - 6000){ //If no ACK in 6 seconds, resend :)
167 | packetToSend.add(pk);
168 | recoveryQueue.remove(seq);
169 | } else {
170 | break;
171 | }
172 | }
173 |
174 | for(Integer seq : receivedWindow.keySet()){
175 | if(seq < windowStart){
176 | receivedWindow.remove(seq);
177 | } else {
178 | break;
179 | }
180 | }
181 |
182 | try {
183 | sendQueue();
184 | } catch (IOException e) {
185 | manager.getLogger().notice("Failed to send queue: IOException: "+e.getMessage());
186 | throw new RuntimeException(e);
187 | }
188 | }
189 |
190 | private void sendPing() throws IOException {
191 | PING_DataPacket ping = new PING_DataPacket();
192 | ping.pingID = Instant.now().toEpochMilli();
193 | ping.encode();
194 |
195 | EncapsulatedPacket pk = new EncapsulatedPacket();
196 | pk.reliability = 0;
197 | pk.buffer = ping.buffer;
198 | addToQueue(pk);
199 | lastPing = Instant.now().toEpochMilli();
200 | }
201 |
202 | public void disconnect(String reason){
203 | manager.streamClose("0.0.0.0:"+manager.getSocket().getSocket().getLocalPort(), reason);
204 | manager.shutdown = true;
205 | }
206 |
207 | public void disconnect() {
208 | disconnect("disconnected by client.");
209 | }
210 |
211 | private void sendPacket(Packet pk) throws IOException {
212 | manager.sendPacket(pk, manager.getClient().getServerIP(), manager.getClient().getServerPort());
213 | }
214 |
215 | public void sendQueue() throws IOException {
216 | if(!sendQueue.packets.isEmpty()){
217 | sendQueue.seqNumber = sendSeqNumber++;
218 | sendPacket(sendQueue);
219 | sendQueue.sendTime = Instant.now().toEpochMilli();
220 | recoveryQueue.put(sendQueue.seqNumber, sendQueue);
221 | sendQueue = new DataPackets.DATA_PACKET_4();
222 | }
223 | }
224 |
225 | private void addToQueue(EncapsulatedPacket pk) throws IOException {
226 | addToQueue(pk, JRakLib.PRIORITY_NORMAL);
227 | }
228 |
229 | private void addToQueue(EncapsulatedPacket pk, int flags) throws IOException {
230 | int priority = flags & 0b0000111;
231 | if(pk.needACK && pk.messageIndex != -1){
232 | Map map;
233 | if(needACK.get(pk.needACK) != null){
234 | map = needACK.get(pk.needACK);
235 | map.put(pk.messageIndex, pk.messageIndex);
236 | } else {
237 | map = new ConcurrentHashMap<>();
238 | map.put(pk.messageIndex, pk.messageIndex);
239 | }
240 | needACK.put(pk.identifierACK, map);
241 | }
242 |
243 | if(priority == JRakLib.PRIORITY_IMMEDIATE){ //Skip queues
244 | DataPacket packet = new DataPackets.DATA_PACKET_0();
245 | packet.seqNumber = sendSeqNumber++;
246 | if(pk.needACK){
247 | packet.packets.add(pk);
248 | pk.needACK = false;
249 | } else {
250 | packet.packets.add(pk.toBinary());
251 | }
252 |
253 | sendPacket(packet);
254 | packet.sendTime = Instant.now().toEpochMilli();
255 | recoveryQueue.put(packet.seqNumber, packet);
256 | return;
257 | }
258 | int length = sendQueue.length();
259 | if(length + pk.getTotalLength() > mtuSize){
260 | sendQueue();
261 | }
262 |
263 | if(pk.needACK){
264 | sendQueue.packets.add(pk);
265 | pk.needACK = false;
266 | } else {
267 | sendQueue.packets.add(pk.toBinary());
268 | }
269 | }
270 |
271 | public void addEncapsulatedToQueue(EncapsulatedPacket packet) throws IOException {
272 | addEncapsulatedToQueue(packet, JRakLib.PRIORITY_NORMAL);
273 | }
274 |
275 | public void addEncapsulatedToQueue(EncapsulatedPacket packet, byte flags) throws IOException {
276 | if((packet.needACK = (flags & JRakLib.FLAG_NEED_ACK) > 0) == true){
277 | needACK.put(packet.identifierACK, new ConcurrentHashMap<>());
278 | }
279 |
280 | if(packet.reliability == 2 || packet.reliability == 3 || packet.reliability == 4 || packet.reliability == 6 || packet.reliability == 7){
281 | packet.messageIndex = messageIndex++;
282 |
283 | if(packet.reliability == 3){
284 | channelIndex.put(packet.orderChannel, channelIndex.get(packet.orderChannel) + 1);
285 | packet.orderIndex = channelIndex.get(packet.orderChannel);
286 | }
287 | }
288 |
289 | if(packet.getTotalLength() + 4 > mtuSize){
290 | byte[][] buffers = Binary.splitbytes(packet.buffer, mtuSize - 34);
291 | int splitID = this.splitID++;
292 | splitID = splitID % 65536;
293 | for(int count = 0; count < buffers.length; count++){
294 | byte[] buffer = buffers[count];
295 | EncapsulatedPacket pk = new EncapsulatedPacket();
296 | pk.splitID = (short) splitID;
297 | pk.hasSplit = true;
298 | pk.splitCount = buffers.length;
299 | pk.reliability = packet.reliability;
300 | pk.splitIndex = count;
301 | pk.buffer = buffer;
302 | if(count > 0){
303 | pk.messageIndex = messageIndex++;
304 | } else {
305 | pk.messageIndex = packet.messageIndex;
306 | }
307 | if(pk.reliability == 3){
308 | pk.orderChannel = packet.orderChannel;
309 | pk.orderIndex = packet.orderIndex;
310 | }
311 | addToQueue(pk, flags | JRakLib.PRIORITY_IMMEDIATE);
312 | }
313 | } else {
314 | addToQueue(packet, flags);
315 | }
316 | }
317 |
318 | private void handleSplit(EncapsulatedPacket packet) throws IOException {
319 | if(packet.splitCount >= 128){
320 | return;
321 | }
322 |
323 | if(!splitPackets.containsKey(packet.splitID)){
324 | Map map = new ConcurrentHashMap<>();
325 | map.put(packet.splitIndex, packet);
326 | splitPackets.put(packet.splitID, map);
327 | } else {
328 | Map map = splitPackets.get(packet.splitID);
329 | map.put(packet.splitIndex, packet);
330 | splitPackets.put(packet.splitID, map);
331 | }
332 |
333 | if(splitPackets.get(packet.splitID).values().size() == packet.splitCount){
334 | EncapsulatedPacket pk = new EncapsulatedPacket();
335 | ByteBuffer bb = ByteBuffer.allocate(64 * 64 * 64);
336 | for(int i = 0; i < packet.splitCount; i++){
337 | bb.put(splitPackets.get(packet.splitID).get(i).buffer);
338 | }
339 | pk.buffer = Arrays.copyOf(bb.array(), bb.position());
340 | bb = null;
341 |
342 | pk.length = pk.buffer.length;
343 | splitPackets.remove(packet.splitID);
344 |
345 | handleEncapsulatedPacketRoute(pk);
346 | }
347 | }
348 |
349 | private void handleEncapsulatedPacket(EncapsulatedPacket packet) throws IOException {
350 | if(packet.messageIndex == -1){
351 | handleEncapsulatedPacketRoute(packet);
352 | } else {
353 | if(packet.messageIndex < reliableWindowStart || packet.messageIndex > reliableWindowEnd){
354 | return;
355 | }
356 |
357 | if((packet.messageIndex - lastReliableIndex) == 1){
358 | lastReliableIndex++;
359 | reliableWindowStart++;
360 | reliableWindowEnd++;
361 | handleEncapsulatedPacketRoute(packet);
362 |
363 | if(!reliableWindow.values().isEmpty()){
364 | //TODO: Implement ksort() ?
365 | //ksort(reliableWindow.values());
366 |
367 | for(Integer index : reliableWindow.keySet()){
368 | EncapsulatedPacket pk = reliableWindow.get(index);
369 |
370 | if((index - lastReliableIndex) != 1){
371 | break;
372 | }
373 | lastReliableIndex++;
374 | reliableWindowStart++;
375 | reliableWindowEnd++;
376 | handleEncapsulatedPacketRoute(packet);
377 | reliableWindow.remove(index);
378 | }
379 | }
380 | } else {
381 | reliableWindow.put(packet.messageIndex, packet);
382 | }
383 | }
384 | }
385 |
386 | private void handleEncapsulatedPacketRoute(EncapsulatedPacket packet) throws IOException {
387 | if(manager == null){
388 | return;
389 | }
390 |
391 | if(packet.hasSplit){
392 | if(state == STATE_CONNECTED){
393 | handleSplit(packet);
394 | }
395 | return;
396 | }
397 |
398 | byte id = packet.buffer[0];
399 | if(id < 0x80) { //internal data packet
400 | if (state == STATE_CONNECTING_3) {
401 | if (id == SERVER_HANDSHAKE_DataPacket.ID) {
402 | SERVER_HANDSHAKE_DataPacket pk = new SERVER_HANDSHAKE_DataPacket();
403 | pk.buffer = packet.buffer;
404 | pk.decode();
405 |
406 | CLIENT_HANDSHAKE_DataPacket response = new CLIENT_HANDSHAKE_DataPacket();
407 | response.address = new InetSocketAddress("0.0.0.0", 0);
408 | response.systemAddresses = new InetSocketAddress[] {
409 | new InetSocketAddress("0.0.0.0", 0),
410 | new InetSocketAddress("0.0.0.0", 0),
411 | new InetSocketAddress("0.0.0.0", 0),
412 | new InetSocketAddress("0.0.0.0", 0),
413 | new InetSocketAddress("0.0.0.0", 0),
414 | new InetSocketAddress("0.0.0.0", manager.getSocket().getSocket().getLocalPort()),
415 | new InetSocketAddress("0.0.0.0", 0),
416 | new InetSocketAddress("0.0.0.0", manager.getSocket().getSocket().getLocalPort()),
417 | new InetSocketAddress("0.0.0.0", 0),
418 | new InetSocketAddress("0.0.0.0", manager.getSocket().getSocket().getLocalPort()),
419 | };
420 | response.sendPing = Instant.now().toEpochMilli();
421 | response.sendPong = Instant.now().toEpochMilli();
422 | response.encode();
423 |
424 | EncapsulatedPacket sendPacket = new EncapsulatedPacket();
425 | sendPacket.reliability = 0;
426 | sendPacket.buffer = response.buffer;
427 | addToQueue(sendPacket, JRakLib.PRIORITY_IMMEDIATE);
428 |
429 | if(!manager.portChecking){
430 | state = STATE_CONNECTED;
431 | manager.streamOpen(this.id);
432 | for(EncapsulatedPacket p : preJoinQueue){
433 | manager.streamEncapsulated(p);
434 | }
435 | preJoinQueue.clear();
436 | }
437 |
438 | }
439 | } else if (id == CLIENT_DISCONNECT_DataPacket.ID) {
440 | disconnect("disconnected by server.");
441 | } else if (id == PING_DataPacket.ID) {
442 | PING_DataPacket dataPacket = new PING_DataPacket();
443 | dataPacket.buffer = packet.buffer;
444 | dataPacket.decode();
445 |
446 | PONG_DataPacket pk = new PONG_DataPacket();
447 | pk.pingID = dataPacket.pingID;
448 | pk.encode();
449 |
450 | EncapsulatedPacket sendPacket = new EncapsulatedPacket();
451 | sendPacket.reliability = 0;
452 | sendPacket.buffer = pk.buffer;
453 | addToQueue(sendPacket);
454 | //TODO: add PING/PONG (0x00/0x03) automatic latency measure
455 | } else if (id == PONG_DataPacket.ID) {
456 | lastPong = Instant.now().toEpochMilli();
457 | } else if(state == STATE_CONNECTED) {
458 | manager.streamEncapsulated(packet);
459 | //TODO: stream channels
460 | }
461 | } else {
462 | preJoinQueue.add(packet);
463 | }
464 | }
465 |
466 | public void handlePacket(Packet packet) throws IOException{
467 | isActive = true;
468 | lastUpdate = Instant.now().toEpochMilli();
469 | if(state == STATE_CONNECTED || state == STATE_CONNECTING_3){
470 | if(packet.buffer[0] >= 0x80 || packet.buffer[0] <= 0x8f && packet instanceof DataPacket){
471 | packet.decode();
472 |
473 | DataPacket dp = (DataPacket) packet;
474 | if(dp.seqNumber < windowStart || dp.seqNumber > windowEnd || receivedWindow.containsKey(dp.seqNumber)){
475 | return;
476 | }
477 |
478 | int diff = dp.seqNumber - lastSeqNumber;
479 |
480 | NACKQueue.remove(Integer.valueOf(dp.seqNumber));
481 | ACKQueue.add(dp.seqNumber);
482 | receivedWindow.put(dp.seqNumber, dp.seqNumber);
483 |
484 | if(diff != 1){
485 | for(int i = lastSeqNumber + 1; i < dp.seqNumber; i++){
486 | if(!receivedWindow.containsKey(i)){
487 | NACKQueue.add(i);
488 | }
489 | }
490 | }
491 |
492 | if(diff >= 1){
493 | lastSeqNumber = dp.seqNumber;
494 | windowStart += diff;
495 | windowEnd += diff;
496 | }
497 |
498 | for(Object pk : dp.packets){
499 | if(pk instanceof EncapsulatedPacket) {
500 | handleEncapsulatedPacket((EncapsulatedPacket) pk);
501 | }
502 | }
503 | } else {
504 | if(packet instanceof ACK){
505 | packet.decode();
506 | for(int seq : ((ACK) packet).packets){
507 | if(recoveryQueue.containsKey(seq)){
508 | for(Object pk : recoveryQueue.get(seq).packets){
509 | if(pk instanceof EncapsulatedPacket && ((EncapsulatedPacket) pk).needACK && ((EncapsulatedPacket) pk).messageIndex != -1){
510 | if(needACK.containsKey(((EncapsulatedPacket) pk).identifierACK)){
511 | Map map = needACK.get(((EncapsulatedPacket) pk).identifierACK);
512 | map.remove(((EncapsulatedPacket) pk).messageIndex);
513 | needACK.put(((EncapsulatedPacket) pk).identifierACK, map);
514 | }
515 | }
516 | recoveryQueue.remove(seq);
517 | }
518 | }
519 | }
520 | } else if(packet instanceof NACK){
521 | packet.decode();
522 | for(Integer seq : ((NACK) packet).packets){
523 | if(recoveryQueue.containsKey(seq)){
524 | DataPacket pk = recoveryQueue.get(seq);
525 | pk.seqNumber = sendSeqNumber++;
526 | packetToSend.add(pk);
527 | recoveryQueue.remove(seq);
528 | }
529 | }
530 | }
531 | }
532 | } else if(packet.buffer[0] > 0x00 || packet.buffer[0] < 0x80){ //Not Data packet :)
533 | packet.decode();
534 | if(packet instanceof OPEN_CONNECTION_REPLY_1){
535 | OPEN_CONNECTION_REPLY_1 reply1 = (OPEN_CONNECTION_REPLY_1) packet;
536 | this.id = reply1.serverID;
537 | OPEN_CONNECTION_REQUEST_2 request2 = new OPEN_CONNECTION_REQUEST_2();
538 | request2.mtuSize = (short) this.mtuSize;
539 | request2.clientID = JRakLibClient.getClientID();
540 | request2.serverAddress = manager.getClient().getServerEndpoint();
541 | request2.encode();
542 | sendPacket(request2);
543 | if(reply1.mtuSize == this.mtuSize){
544 | state = STATE_CONNECTING_2;
545 | }
546 | } else if(state == STATE_CONNECTING_2 && packet instanceof OPEN_CONNECTION_REPLY_2){
547 | OPEN_CONNECTION_REPLY_2 reply2 = (OPEN_CONNECTION_REPLY_2) packet;
548 | if(((OPEN_CONNECTION_REPLY_2) packet).clientAddress.getPort() == manager.getSocket().getSocket().getLocalPort() || !manager.portChecking){
549 | CLIENT_CONNECT_DataPacket connect = new CLIENT_CONNECT_DataPacket();
550 | connect.clientID = JRakLibClient.getClientID();
551 | connect.sendPing = sessionID;
552 | connect.encode();
553 |
554 | EncapsulatedPacket pk = new EncapsulatedPacket();
555 | pk.reliability = 0;
556 | pk.buffer = connect.buffer;
557 | addToQueue(pk, JRakLib.PRIORITY_IMMEDIATE);
558 | sendPing();
559 | state = STATE_CONNECTING_3;
560 | }
561 | }
562 | }
563 | }
564 |
565 | protected void onShutdown() {
566 | try {
567 | close();
568 | } catch (IOException e) {
569 | manager.getClient().getLogger().critical("Could not close connection to server, IOException: "+e.getMessage());
570 | e.printStackTrace();
571 | }
572 | }
573 |
574 | public void close() throws IOException {
575 | byte[] data = new byte[] {0x00, 0x00, 0x08, 0x15};
576 | addEncapsulatedToQueue(EncapsulatedPacket.fromBinary(data), JRakLib.PRIORITY_IMMEDIATE);
577 | sendQueue();
578 | manager = null;
579 | }
580 | }
581 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/client/ConnectionManager.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.client;
21 |
22 | import static net.beaconpe.jraklib.JRakLib.getAddressFromString;
23 | import static net.beaconpe.jraklib.JRakLib.sleepUntil;
24 | import static net.beaconpe.jraklib.protocol.DataPackets.*;
25 |
26 | import net.beaconpe.jraklib.Binary;
27 | import net.beaconpe.jraklib.JRakLib;
28 | import net.beaconpe.jraklib.Logger;
29 | import net.beaconpe.jraklib.protocol.*;
30 |
31 | import java.io.IOException;
32 | import java.net.DatagramPacket;
33 | import java.net.InetSocketAddress;
34 | import java.net.SocketAddress;
35 | import java.nio.ByteBuffer;
36 | import java.time.Instant;
37 | import java.util.Arrays;
38 | import java.util.ConcurrentModificationException;
39 | import java.util.Map;
40 | import java.util.concurrent.ConcurrentHashMap;
41 | import java.util.regex.Pattern;
42 |
43 | /**
44 | * Manager for handling a connection. The manager handles the internal ticking, and networking.
45 | *
46 | * @author jython234
47 | */
48 | public class ConnectionManager {
49 | protected Map> packetPool = new ConcurrentHashMap<>();
50 |
51 | protected JRakLibClient client;
52 |
53 | protected UDPClientSocket socket;
54 |
55 | protected int receiveBytes = 0;
56 | protected int sendBytes = 0;
57 |
58 | protected boolean shutdown = false;
59 |
60 | protected int ticks = 0;
61 | protected long lastMeasure;
62 |
63 | protected Connection connection;
64 |
65 | public boolean portChecking = false;
66 |
67 | public ConnectionManager(JRakLibClient client, UDPClientSocket socket){
68 | this.client = client;
69 | this.socket = socket;
70 | registerPackets();
71 |
72 | try {
73 | if(!connect(1447, 4)){
74 | getLogger().notice("Failed to connect to "+client.getServerEndpoint()+": no response.");
75 | client.pushMainToThreadPacket(new byte[] {JRakLib.PACKET_SHUTDOWN});
76 | } else {
77 | run();
78 | }
79 | } catch (IOException e) {
80 | getLogger().emergency("*** FAILED TO CONNECT TO " + client.getServerEndpoint() + ": IOException: " + e.getMessage());
81 | e.printStackTrace();
82 | client.pushMainToThreadPacket(new byte[]{JRakLib.PACKET_EMERGENCY_SHUTDOWN});
83 | }
84 | }
85 |
86 | private boolean connect(int payloadSize, int packets) throws IOException {
87 | for(int i = 0; i < packets; i++){
88 | OPEN_CONNECTION_REQUEST_1 request1 = new OPEN_CONNECTION_REQUEST_1();
89 | request1.protocol = JRakLib.PROTOCOL;
90 | request1.mtuSize = (short) payloadSize;
91 | request1.encode();
92 | socket.writePacket(request1.buffer, client.getServerEndpoint());
93 |
94 | DatagramPacket response = socket.readPacketBlocking(500);
95 | if(response != null && response.getData()[0] == OPEN_CONNECTION_REPLY_1.ID){
96 | connection = new Connection(this, payloadSize);
97 | Packet packet = getPacketFromPool(response.getData()[0]);
98 | packet.buffer = response.getData();
99 | connection.handlePacket(packet);
100 | return true;
101 | }
102 | }
103 | if(payloadSize == 1447){
104 | return connect(1155, 4);
105 | } else if(payloadSize == 1155){
106 | return connect(531, 5);
107 | } else {
108 | return false;
109 | }
110 | }
111 |
112 | public Logger getLogger(){
113 | return client.getLogger();
114 | }
115 |
116 | public void run(){
117 | try {
118 | tickProcessor();
119 | } catch (Exception e) {
120 | throw new RuntimeException(e);
121 | }
122 | }
123 |
124 | private void tickProcessor() throws IOException {
125 | lastMeasure = Instant.now().toEpochMilli();
126 |
127 | while(!shutdown){
128 | long start = Instant.now().toEpochMilli();
129 | int max = 5000;
130 | while(receivePacket()){
131 | max = max - 1;
132 | }
133 | while(receiveStream());
134 | long time = Instant.now().toEpochMilli() - start;
135 | if(time < 50){ //20 ticks per second (1000 / 20)
136 | sleepUntil(Instant.now().toEpochMilli()+(50 - time));
137 | }
138 | tick();
139 | }
140 | }
141 |
142 | private void tick() throws IOException {
143 | long time = Instant.now().toEpochMilli();
144 | connection.update(time);
145 |
146 | if((ticks & 0b1111) == 0){
147 | double diff = Math.max(0.005d, time - lastMeasure);
148 | streamOption("bandwith", "up:"+(sendBytes / diff)+",down:"+(receiveBytes / diff)); //TODO: Fix this stuff
149 | lastMeasure = time;
150 | sendBytes = 0;
151 | receiveBytes = 0;
152 | }
153 | ticks = ticks + 1;
154 | }
155 |
156 | private boolean receivePacket() throws IOException {
157 | DatagramPacket packet = socket.readPacket();
158 | if(packet == null) {
159 | return false;
160 | }
161 | int len = packet.getLength();
162 | if(len > 0){
163 | SocketAddress source = packet.getSocketAddress();
164 | receiveBytes += len;
165 |
166 | Packet pkt = getPacketFromPool(packet.getData()[0]);
167 | if(pkt != null){
168 | pkt.buffer = packet.getData();
169 | connection.handlePacket(pkt);
170 | return true;
171 | } else if (packet.getData() != null){
172 | streamRaw(source, packet.getData());
173 | return true;
174 | } else {
175 | getLogger().notice("Dropped packet: "+ Arrays.toString(packet.getData()));
176 | return false;
177 | }
178 | }
179 | return false;
180 | }
181 |
182 | public void streamEncapsulated(EncapsulatedPacket packet){
183 | streamEncapsulated(packet, JRakLib.PRIORITY_NORMAL);
184 | }
185 |
186 | public void streamEncapsulated(EncapsulatedPacket packet, byte flags){
187 | String id = client.getServerIP() + ":" + client.getServerPort();
188 | ByteBuffer bb = ByteBuffer.allocate(3+id.getBytes().length+packet.getTotalLength(true));
189 | bb.put(JRakLib.PACKET_ENCAPSULATED).put((byte) id.getBytes().length).put(id.getBytes()).put(flags).put(packet.toBinary(true));
190 | client.pushThreadToMainPacket(bb.array());
191 | }
192 |
193 | public void streamRaw(SocketAddress address, byte[] payload){
194 | String dest;
195 | int port;
196 | if(address.toString().contains("/")) {
197 | dest = address.toString().split(Pattern.quote("/"))[1].split(Pattern.quote(":"))[0];
198 | port = Integer.parseInt(address.toString().split(Pattern.quote("/"))[1].split(Pattern.quote(":"))[1]);
199 | } else {
200 | dest = address.toString().split(Pattern.quote(":"))[0];
201 | port = Integer.parseInt(address.toString().split(Pattern.quote(":"))[1]);
202 | }
203 | streamRaw(dest, port, payload);
204 | }
205 |
206 | public void streamRaw(String address, int port, byte[] payload){
207 | ByteBuffer bb = ByteBuffer.allocate(4 + address.getBytes().length + payload.length);
208 | bb.put(JRakLib.PACKET_RAW).put((byte) address.getBytes().length).put(address.getBytes()).put(Binary.writeShort((short) port)).put(payload);
209 | client.pushThreadToMainPacket(bb.array());
210 | }
211 |
212 | protected void streamClose(String identifier, String reason){
213 | ByteBuffer bb = ByteBuffer.allocate(3 + identifier.getBytes().length + reason.getBytes().length);
214 | bb.put(JRakLib.PACKET_CLOSE_SESSION).put((byte) identifier.getBytes().length).put(identifier.getBytes()).put((byte) reason.getBytes().length).put(reason.getBytes());
215 | client.pushThreadToMainPacket(bb.array());
216 | }
217 |
218 | protected void streamInvalid(String identifier){
219 | ByteBuffer bb = ByteBuffer.allocate(2+identifier.getBytes().length);
220 | bb.put(JRakLib.PACKET_INVALID_SESSION).put((byte) identifier.getBytes().length).put(identifier.getBytes());
221 | client.pushThreadToMainPacket(bb.array());
222 | }
223 |
224 | protected void streamOpen(long serverId){
225 | String identifier = client.getServerIP() + ":" + client.getServerPort();
226 | ByteBuffer bb = ByteBuffer.allocate(10 + identifier.getBytes().length);
227 | bb.put(JRakLib.PACKET_OPEN_SESSION).put((byte) identifier.getBytes().length).put(identifier.getBytes()).put(Binary.writeLong(serverId));
228 | client.pushThreadToMainPacket(bb.array());
229 | }
230 |
231 | protected void streamACK(String identifier, int identifierACK){
232 | ByteBuffer bb = ByteBuffer.allocate(6+identifier.getBytes().length);
233 | bb.put(JRakLib.PACKET_ACK_NOTIFICATION).put((byte) identifier.getBytes().length).put(identifier.getBytes()).put(Binary.writeInt(identifierACK));
234 | client.pushThreadToMainPacket(bb.array());
235 | }
236 |
237 | protected void streamOption(String name, String value){
238 | ByteBuffer bb = ByteBuffer.allocate(2+name.getBytes().length+value.getBytes().length);
239 | bb.put(JRakLib.PACKET_SET_OPTION).put((byte) name.getBytes().length).put(name.getBytes()).put(value.getBytes());
240 | client.pushThreadToMainPacket(bb.array());
241 | }
242 |
243 | public void sendPacket(Packet packet, String dest, int port) throws IOException {
244 | packet.encode();
245 | sendBytes += packet.buffer.length;
246 | socket.writePacket(packet.buffer, new InetSocketAddress(dest, port));
247 | }
248 |
249 | public boolean receiveStream() throws IOException {
250 | byte[] packet = client.readMainToThreadPacket();
251 | if(packet == null){
252 | return false;
253 | }
254 | if(packet.length > 0){
255 | byte id = packet[0];
256 | int offset = 1;
257 | if(id == JRakLib.PACKET_ENCAPSULATED){
258 | int len = packet[offset++];
259 | String identifier = new String(Binary.subbytes(packet, offset, len));
260 | offset += len;
261 | byte flags = packet[offset++];
262 | byte[] buffer = Binary.subbytes(packet, offset);
263 | connection.addEncapsulatedToQueue(EncapsulatedPacket.fromBinary(buffer, true), flags);
264 | } else if(id == JRakLib.PACKET_RAW){
265 | int len = packet[offset++];
266 | String address = new String(Binary.subbytes(packet, offset, len));
267 | offset += len;
268 | int port = Binary.readShort(Binary.subbytes(packet, offset, 2));
269 | offset += 2;
270 | byte[] payload = Binary.subbytes(packet, offset);
271 | socket.writePacket(payload, new InetSocketAddress(address, port));
272 | } else if(id == JRakLib.PACKET_CLOSE_SESSION){
273 | /*
274 | int len = packet[offset++];
275 | String identifier = new String(Binary.subbytes(packet, offset, len));
276 | */
277 | client.pushThreadToMainPacket(packet);
278 | } else if(id == JRakLib.PACKET_SET_OPTION){
279 | int len = packet[offset++];
280 | String name = new String(Binary.subbytes(packet, offset, len));
281 | offset += len;
282 | String value = new String(Binary.subbytes(packet, offset));
283 | switch(name){
284 | case "portChecking":
285 | portChecking = Boolean.parseBoolean(value);
286 | break;
287 | }
288 | } else if(id == JRakLib.PACKET_SHUTDOWN){
289 | connection.onShutdown();
290 |
291 | socket.close();
292 | shutdown = true;
293 | } else if(id == JRakLib.PACKET_EMERGENCY_SHUTDOWN){
294 | shutdown = true;
295 | } else {
296 | return false;
297 | }
298 | return true;
299 | }
300 | return false;
301 | }
302 |
303 | private void registerPacket(byte id, Class extends Packet> clazz){
304 | packetPool.put(id, clazz);
305 | }
306 |
307 | public Packet getPacketFromPool(byte id){
308 | if(packetPool.containsKey(id)){
309 | try {
310 | return packetPool.get(id).newInstance();
311 | } catch (InstantiationException e) {
312 | e.printStackTrace();
313 | } catch (IllegalAccessException e) {
314 | e.printStackTrace();
315 | }
316 | }
317 | return null;
318 | }
319 |
320 | private void registerPackets() {
321 | registerPacket(UNCONNECTED_PING.ID, UNCONNECTED_PING.class);
322 | registerPacket(UNCONNECTED_PING_OPEN_CONNECTIONS.ID, UNCONNECTED_PING_OPEN_CONNECTIONS.class);
323 | registerPacket(OPEN_CONNECTION_REQUEST_1.ID, OPEN_CONNECTION_REQUEST_1.class);
324 | registerPacket(OPEN_CONNECTION_REPLY_1.ID, OPEN_CONNECTION_REPLY_1.class);
325 | registerPacket(OPEN_CONNECTION_REQUEST_2.ID, OPEN_CONNECTION_REQUEST_2.class);
326 | registerPacket(OPEN_CONNECTION_REPLY_2.ID, OPEN_CONNECTION_REPLY_2.class);
327 | registerPacket(UNCONNECTED_PONG.ID, UNCONNECTED_PONG.class);
328 | registerPacket(ADVERTISE_SYSTEM.ID, ADVERTISE_SYSTEM.class);
329 | registerPacket(DATA_PACKET_0.ID, DATA_PACKET_0.class);
330 | registerPacket(DATA_PACKET_1.ID, DATA_PACKET_1.class);
331 | registerPacket(DATA_PACKET_2.ID, DATA_PACKET_2.class);
332 | registerPacket(DATA_PACKET_3.ID, DATA_PACKET_3.class);
333 | registerPacket(DATA_PACKET_4.ID, DATA_PACKET_4.class);
334 | registerPacket(DATA_PACKET_5.ID, DATA_PACKET_5.class);
335 | registerPacket(DATA_PACKET_6.ID, DATA_PACKET_6.class);
336 | registerPacket(DATA_PACKET_7.ID, DATA_PACKET_7.class);
337 | registerPacket(DATA_PACKET_8.ID, DATA_PACKET_8.class);
338 | registerPacket(DATA_PACKET_9.ID, DATA_PACKET_9.class);
339 | registerPacket(DATA_PACKET_A.ID, DATA_PACKET_A.class);
340 | registerPacket(DATA_PACKET_B.ID, DATA_PACKET_B.class);
341 | registerPacket(DATA_PACKET_C.ID, DATA_PACKET_C.class);
342 | registerPacket(DATA_PACKET_D.ID, DATA_PACKET_D.class);
343 | registerPacket(DATA_PACKET_E.ID, DATA_PACKET_E.class);
344 | registerPacket(DATA_PACKET_F.ID, DATA_PACKET_F.class);
345 | registerPacket(NACK.ID, NACK.class);
346 | registerPacket(ACK.ID, ACK.class);
347 | }
348 |
349 | public JRakLibClient getClient() {
350 | return client;
351 | }
352 |
353 | public UDPClientSocket getSocket() {
354 | return socket;
355 | }
356 | }
357 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/client/JRakLibClient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.client;
21 |
22 | import net.beaconpe.jraklib.Logger;
23 | import net.beaconpe.jraklib.protocol.UNCONNECTED_PING;
24 | import net.beaconpe.jraklib.protocol.UNCONNECTED_PONG;
25 |
26 | import java.io.IOException;
27 | import java.net.DatagramPacket;
28 | import java.net.InetSocketAddress;
29 | import java.util.LinkedList;
30 | import java.util.List;
31 | import java.util.Random;
32 |
33 | /**
34 | * Represents a JRakLib Client.
35 | * @author jython234
36 | */
37 | public class JRakLibClient extends Thread{
38 | private static long clientID = new Random(System.currentTimeMillis()).nextLong();
39 | private static long startTime = -1;
40 | protected InetSocketAddress serverEndpoint;
41 |
42 | protected Logger logger;
43 | protected boolean shutdown = false;
44 |
45 | protected List externalQueue;
46 | protected List internalQueue;
47 |
48 | /**
49 | * Creates a new JRakLibClient and connects right away.
50 | * @param logger Logger implementation for the client.
51 | * @param serverIP The server IP address.
52 | * @param serverPort The server's port.
53 | */
54 | public JRakLibClient(Logger logger, String serverIP, int serverPort){
55 | if(serverPort < 1 || serverPort > 65536){
56 | throw new IllegalArgumentException("Invalid port range.");
57 | }
58 | this.logger = logger;
59 | this.serverEndpoint = new InetSocketAddress(serverIP, serverPort);
60 |
61 | externalQueue = new LinkedList<>();
62 | internalQueue = new LinkedList<>();
63 |
64 | start();
65 | }
66 |
67 | /**
68 | * Pings a RakNet server at the specified serverIP
and serverPort
.
69 | * @param logger Logger implementation.
70 | * @param serverIP The server's IP Address.
71 | * @param serverPort The server's port.
72 | * @param tries Amount of packets sent with no response before giving up. (tries)
73 | * @param delay Delay between packets sent with no response. (In milliseconds)
74 | * @throws IOException If there is a problem while reading/sending.
75 | * @return The ping response
76 | */
77 | public static PingResponse pingServer(Logger logger, String serverIP, int serverPort, int tries, int delay) throws IOException {
78 | if(startTime == -1){
79 | startTime = System.currentTimeMillis();
80 | }
81 | UDPClientSocket socket = new UDPClientSocket(logger);
82 | for(int i = 0; i < tries; i++){
83 | UNCONNECTED_PING ping = new UNCONNECTED_PING();
84 | ping.pingId = System.currentTimeMillis() - startTime; //Amount since start.
85 | ping.encode();
86 | socket.writePacket(ping.buffer, new InetSocketAddress(serverIP, serverPort));
87 |
88 | DatagramPacket pkt = socket.readPacketBlocking(delay);
89 | if(pkt != null && pkt.getData()[0] == UNCONNECTED_PONG.ID){
90 | UNCONNECTED_PONG pong = new UNCONNECTED_PONG();
91 | pong.buffer = pkt.getData();
92 | pong.decode();
93 | return new PingResponse(pong.serverID, pong.pingID, pong.serverName);
94 | }
95 | }
96 | return null;
97 | }
98 |
99 | public boolean isShutdown(){
100 | return shutdown == true;
101 | }
102 |
103 | public void shutdown(){
104 | shutdown = true;
105 | }
106 |
107 | public int getServerPort(){
108 | return serverEndpoint.getPort();
109 | }
110 |
111 | public String getServerIP(){
112 | return serverEndpoint.getHostString();
113 | }
114 |
115 | public InetSocketAddress getServerEndpoint() {
116 | return serverEndpoint;
117 | }
118 |
119 | public Logger getLogger(){
120 | return logger;
121 | }
122 |
123 | public List getExternalQueue(){
124 | return externalQueue;
125 | }
126 |
127 | public List getInternalQueue(){
128 | return internalQueue;
129 | }
130 |
131 | public void pushMainToThreadPacket(byte[] bytes){
132 | internalQueue.add(0, bytes);
133 | }
134 |
135 | public byte[] readMainToThreadPacket(){
136 | if(!internalQueue.isEmpty()) {
137 | byte[] data = internalQueue.get(internalQueue.size() - 1);
138 | internalQueue.remove(data);
139 | return data;
140 | }
141 | return null;
142 | }
143 |
144 | public void pushThreadToMainPacket(byte[] bytes){
145 | externalQueue.add(0, bytes);
146 | }
147 |
148 | public byte[] readThreadToMainPacket(){
149 | if(!externalQueue.isEmpty()) {
150 | byte[] data = externalQueue.get(externalQueue.size() - 1);
151 | externalQueue.remove(data);
152 | return data;
153 | }
154 | return null;
155 | }
156 |
157 | /**
158 | * Regenerates the static clientID.
159 | */
160 | public static void regenerateClientID(){
161 | clientID = new Random().nextLong();
162 | }
163 |
164 | /**
165 | * Regenerates the static clientID with the specified seed.
166 | * @param seed The seed to generate the clientID.
167 | */
168 | public static void regenerateClientID(long seed){
169 | clientID = new Random(seed).nextLong();
170 | }
171 |
172 | public static long getClientID(){
173 | return clientID;
174 | }
175 |
176 | public long getTimeSinceStart() {
177 | return startTime;
178 | }
179 |
180 | private class ShutdownHandler extends Thread {
181 | @Override
182 | public void run() {
183 | if (shutdown != true) {
184 | logger.emergency("[JRakLib Thread #" + getId() + "] JRakLib crashed!");
185 | }
186 | }
187 | }
188 |
189 | /**
190 | * Represents a PingResponse from a server.
191 | */
192 | public static class PingResponse {
193 | /**
194 | * The PingId from the packet. The original amount sent to the server was the time sent start. (in milliseconds)
195 | *
196 | * You can find more information here: http://wiki.vg/Pocket_Minecraft_Protocol#ID_CONNECTED_PING_OPEN_CONNECTIONS_.280x01.29
197 | */
198 | public final long pingId;
199 | /**
200 | * The serverId from the packet. This is the server's unique identifier generated at startup. (The value changes each runtime, and could be modified in transport)
201 | */
202 | public final long serverId;
203 | /**
204 | * The server's name or MOTD.
205 | */
206 | public final String name;
207 |
208 | public PingResponse(long serverId, long pingId, String name){
209 | this.pingId = pingId;
210 | this.serverId = serverId;
211 | this.name = name;
212 | }
213 | }
214 |
215 | @Override
216 | public void run(){
217 | setName("JRakLib Client Thread #"+getId());
218 | Runtime.getRuntime().addShutdownHook(new ShutdownHandler());
219 | UDPClientSocket socket = new UDPClientSocket(logger);
220 | new ConnectionManager(this, socket);
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/client/UDPClientSocket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.client;
21 |
22 | import net.beaconpe.jraklib.Logger;
23 |
24 | import javax.xml.crypto.Data;
25 | import java.io.Closeable;
26 | import java.io.IOException;
27 | import java.net.*;
28 | import java.util.Arrays;
29 | import java.util.ConcurrentModificationException;
30 |
31 | /**
32 | * A UDP Client socket.
33 | *
34 | * @author jython234
35 | */
36 | public class UDPClientSocket implements Closeable{
37 | private Logger logger;
38 | private DatagramSocket socket;
39 |
40 | /**
41 | * Create a new UDP Client socket with the specified logger.
42 | * @param logger The logger used in case the socket was unable to be created.
43 | */
44 | public UDPClientSocket(Logger logger){
45 | this.logger = logger;
46 | try {
47 | socket = new DatagramSocket();
48 | socket.setBroadcast(true);
49 | socket.setSendBufferSize(1024 * 1024 * 8);
50 | socket.setReceiveBufferSize(1024 * 1024 * 8);
51 | socket.setSoTimeout(1);
52 | } catch (SocketException e) {
53 | logger.critical("**** FAILED TO CREATE SOCKET!");
54 | logger.critical("java.net.SocketException: "+e.getMessage());
55 | System.exit(1);
56 | }
57 | }
58 |
59 | /**
60 | * Returns the socket used by this client socket.
61 | * @return DatagramSocket
62 | */
63 | public DatagramSocket getSocket(){
64 | return socket;
65 | }
66 |
67 | /**
68 | * Closes this socket, and the underlying DatagramSocket.
69 | */
70 | @Override
71 | public void close() {
72 | socket.close();
73 | }
74 |
75 | /**
76 | * Reads a packet from the socket (non-blocking). If no packet is received, the method will return null.
77 | * @return The DatagramPacket, if received.
78 | * @throws IOException If there is a problem while reading.
79 | */
80 | public DatagramPacket readPacket() throws IOException {
81 | DatagramPacket dp = new DatagramPacket(new byte[65535], 65535);
82 | try {
83 | socket.receive(dp);
84 | dp.setData(Arrays.copyOf(dp.getData(), dp.getLength()));
85 | return dp;
86 | } catch (SocketTimeoutException e) {
87 | return null;
88 | }
89 | }
90 |
91 | /**
92 | * Reads a packet from the socket (blocking). If no packet is received, the method will return null.
93 | * @param blockFor Block for this amount (milliseconds). If amount is negative, the method will block forever (until a packet is received)
94 | * @return The DatagramPacket, if received.
95 | * @throws IOException If there is a problem while reading.
96 | */
97 | public DatagramPacket readPacketBlocking(int blockFor) throws IOException{
98 | DatagramPacket dp = new DatagramPacket(new byte[65535], 65535);
99 | try {
100 | socket.setSoTimeout(blockFor);
101 | socket.receive(dp);
102 | socket.setSoTimeout(1);
103 | dp.setData(Arrays.copyOf(dp.getData(), dp.getLength()));
104 | return dp;
105 | } catch (SocketTimeoutException e) {
106 | return null;
107 | }
108 | }
109 |
110 | /**
111 | * Sends a packet to the specified dest
.
112 | * @param buffer Raw payload of the packet.
113 | * @param dest The endpoint where the data will go.
114 | * @throws IOException If there is a problem while sending.
115 | */
116 | public void writePacket(byte[] buffer, SocketAddress dest) throws IOException {
117 | DatagramPacket dp = new DatagramPacket(buffer, buffer.length, dest);
118 | socket.send(dp);
119 | }
120 |
121 | /**
122 | * Sets this socket's send buffer size. Defaults to 1024 * 1024 * 8.
123 | * @param size Size in bytes.
124 | * @throws SocketException If there is a failure.
125 | */
126 | public void setSendBuffer(int size) throws SocketException {
127 | socket.setSendBufferSize(size);
128 | }
129 |
130 | /**
131 | * Sets this socket's receive buffer size. Defaults to 1024 * 1024 * 8.
132 | * @param size Size in bytes.
133 | * @throws SocketException If there is a failure.
134 | */
135 | public void setRecvBuffer(int size) throws SocketException {
136 | socket.setReceiveBufferSize(size);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/protocol/ACK.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.protocol;
21 |
22 | /**
23 | * Acknowledged.
24 | */
25 | public class ACK extends AcknowledgePacket{
26 | public static byte ID = (byte) 0xc0;
27 |
28 | public byte getID(){
29 | return (byte) 0xc0;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/protocol/ADVERTISE_SYSTEM.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.protocol;
21 |
22 | /**
23 | * Variation of UNCONNECTED_PONG.
24 | */
25 | public class ADVERTISE_SYSTEM extends UNCONNECTED_PONG{
26 | public static byte ID = 0x1d;
27 |
28 | public byte getID() {
29 | return 0x1d;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/protocol/AcknowledgePacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.protocol;
21 |
22 | import net.beaconpe.jraklib.Binary;
23 |
24 | import java.nio.ByteBuffer;
25 | import java.util.ArrayList;
26 | import java.util.Arrays;
27 | import java.util.List;
28 |
29 | /**
30 | * Base class for ACK/NACK.
31 | */
32 | public abstract class AcknowledgePacket extends Packet{
33 |
34 | public Integer[] packets;
35 |
36 | @Override
37 | protected void _encode() {
38 | ByteBuffer payload = ByteBuffer.allocate(1024);
39 | int count = packets.length;
40 | int records = 0;
41 |
42 | if(count > 0){
43 | int pointer = 0;
44 | int start = packets[0];
45 | int last = packets[0];
46 |
47 | while(pointer + 1 < count){
48 | int current = packets[pointer++];
49 | int diff = current - last;
50 | if(diff == 1){
51 | last = current;
52 | } else if(diff > 1){ //Forget about duplicated packets (bad queues?)
53 | if(start == last){
54 | payload.put((byte) 0x01);
55 | payload.put(Binary.writeLTriad(start));
56 | start = last = current;
57 | } else {
58 | payload.put((byte) 0x00);
59 | payload.put(Binary.writeLTriad(start));
60 | payload.put(Binary.writeLTriad(last));
61 | start = last = current;
62 | }
63 | records = records + 1;
64 | }
65 | }
66 |
67 | if(start == last){
68 | payload.put((byte) 0x01);
69 | payload.put(Binary.writeLTriad(start));
70 | } else {
71 | payload.put((byte) 0x00);
72 | payload.put(Binary.writeLTriad(start));
73 | payload.put(Binary.writeLTriad(last));
74 | }
75 | records = records + 1;
76 | }
77 | putShort((short) records);
78 | put(Arrays.copyOf(payload.array(), payload.position()));
79 | }
80 |
81 | @Override
82 | protected void _decode() {
83 | int count = getShort();
84 | List packets = new ArrayList<>();
85 | int cnt = 0;
86 | for(int i = 0; i < count && !feof() && cnt < 4096; i++){
87 | if(getByte() == 0){
88 | int start = getLTriad();
89 | int end = getLTriad();
90 | if((end - start) > 512){
91 | end = start + 512;
92 | }
93 | for(int c = start; c <= end; c++){
94 | cnt = cnt + 1;
95 | packets.add(c);
96 | }
97 | } else {
98 | packets.add(getLTriad());
99 | }
100 | }
101 | this.packets = packets.stream().toArray(Integer[]::new);
102 | }
103 |
104 | public void clean(){
105 | packets = new Integer[] {};
106 | super.clean();
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/protocol/CLIENT_CONNECT_DataPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.protocol;
21 |
22 | /**
23 | * CLIENT_CONNECT (Encapsulated, 0x09)
24 | */
25 | public class CLIENT_CONNECT_DataPacket extends Packet{
26 | public static byte ID = 0x09;
27 |
28 | public long clientID;
29 | public long sendPing;
30 | public boolean useSecurity = false;
31 |
32 |
33 | public byte getID() {
34 | return 0x09;
35 | }
36 |
37 | @Override
38 | protected void _encode() {
39 | putLong(clientID);
40 | putLong(sendPing);
41 | putByte((byte) (useSecurity ? 1 : 0));
42 | }
43 |
44 | @Override
45 | protected void _decode() {
46 | clientID = getLong();
47 | sendPing = getLong();
48 | useSecurity = getByte() > 0;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/protocol/CLIENT_DISCONNECT_DataPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.protocol;
21 |
22 | /**
23 | * CLIENT_DISCONNECT (Encapsulated, 0x15)
24 | */
25 | public class CLIENT_DISCONNECT_DataPacket extends Packet{
26 | public static byte ID = 0x15;
27 |
28 | public byte getID() {
29 | return 0x15;
30 | }
31 |
32 | @Override
33 | protected void _encode() {
34 |
35 | }
36 |
37 | @Override
38 | protected void _decode() {
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/protocol/CLIENT_HANDSHAKE_DataPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.protocol;
21 |
22 | import java.net.InetSocketAddress;
23 |
24 | /**
25 | * CLIENT_HANDSHAKE (Encapsulated, 0x13)
26 | */
27 | public class CLIENT_HANDSHAKE_DataPacket extends Packet{
28 | public static byte ID = 0x13;
29 | public InetSocketAddress address;
30 |
31 | public InetSocketAddress[] systemAddresses;
32 |
33 | public long sendPing;
34 | public long sendPong;
35 |
36 |
37 | public byte getID() {
38 | return 0x13;
39 | }
40 |
41 | @Override
42 | protected void _encode() {
43 | putAddress(address.getHostString(), address.getPort(), (byte) 4);
44 | for(InetSocketAddress a : systemAddresses){
45 | putAddress(a.getHostString(), a.getPort(), (byte) 4);
46 | }
47 | putLong(sendPing);
48 | putLong(sendPong);
49 | }
50 |
51 | @Override
52 | protected void _decode() {
53 | address = getAddress();
54 | systemAddresses = new InetSocketAddress[10];
55 | for(int i = 0; i < 10; i++){
56 | systemAddresses[i] = getAddress();
57 | }
58 | sendPing = getLong();
59 | sendPong = getLong();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/net/beaconpe/jraklib/protocol/DataPacket.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JRakLib is not affiliated with Jenkins Software LLC or RakNet.
3 | * This software is a port of RakLib https://github.com/PocketMine/RakLib.
4 |
5 | * This file is part of JRakLib.
6 | *
7 | * JRakLib is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * JRakLib is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with JRakLib. If not, see .
19 | */
20 | package net.beaconpe.jraklib.protocol;
21 |
22 | import net.beaconpe.jraklib.Binary;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | /**
28 | * Represents a RakNet Custom Packet.
29 | */
30 | public abstract class DataPacket extends Packet{
31 | public List