Skip to content

Commit 2b99a25

Browse files
committed
Update to use NotNullByDefault & Docs update
Update to v1.4.0 This also fixes redundant assignments and provides javadoc comments for side effects from null values least in BungeeProtocol. Changes how requests Forward requires less than an unsigned short of length. Adds tests for forward data length. Adds static final for legacy/modern channels. Unsure if its required on any proxy to send from the non legacy one at this point. Adds javadocs for ONLINE/ALL
1 parent 7c3e8ea commit 2b99a25

File tree

8 files changed

+253
-145
lines changed

8 files changed

+253
-145
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group = "dev.kerman"
7-
version = "1.3.1"
7+
version = "1.4.0"
88

99
repositories {
1010
mavenCentral()

src/main/java/dev/kerman/freight/BungeeMessage.java

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import net.minestom.server.network.packet.server.common.PluginMessagePacket;
88
import net.minestom.server.network.player.PlayerConnection;
99
import net.minestom.server.utils.PacketSendingUtils;
10-
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Contract;
1111
import org.jetbrains.annotations.Nullable;
1212

1313
import java.util.ArrayList;
@@ -66,10 +66,22 @@
6666
* Some notes about this class include attempting to hide all the serialization implementation behind the protocol,
6767
* We still expose all the serializers for the requests and responses in case you want to use them, but don't expect them
6868
* to be stable, but they will probably be stable as the messaging system is likely to not change.
69+
* <br>
70+
* Assume all types to be without identity as these classes are going to be value types; which identity required operations may fail.
6971
*/
7072
public sealed interface BungeeMessage permits BungeeRequest, BungeeResponse {
71-
String ALL = "ALL";
72-
String ONLINE = "ONLINE";
73+
/**
74+
* Represents the ALL identity
75+
* <br>
76+
* You should use the helper methods like {@link BungeeRequest.Forward#all(PluginMessagePacket)} where possible.
77+
*/
78+
String ALL = BungeeProtocol.ALL;
79+
/**
80+
* Represents the ONLINE identity
81+
* <br>
82+
* You should use the helper methods like {@link BungeeRequest.Forward#online(PluginMessagePacket)} where possible.
83+
*/
84+
String ONLINE = BungeeProtocol.ONLINE;
7385

7486
/**
7587
* Checks if the given channel is a valid BungeeCord identifier.
@@ -83,6 +95,7 @@ public sealed interface BungeeMessage permits BungeeRequest, BungeeResponse {
8395
* @param channel the channel to check, can be null
8496
* @return true if the channel is a valid BungeeCord identifier, false otherwise
8597
*/
98+
@Contract(pure = true)
8699
static boolean isIdentifier(@Nullable String channel) {
87100
return BungeeProtocol.isIdentifier(channel);
88101
}
@@ -93,8 +106,10 @@ static boolean isIdentifier(@Nullable String channel) {
93106
*
94107
* @param message the message to write
95108
* @return the byte array containing the serialized message
109+
* @throws NullPointerException if {@code message} is null
96110
*/
97-
static byte @NotNull [] write(@NotNull BungeeMessage message) {
111+
@Contract(pure = true)
112+
static byte[] write(BungeeMessage message) {
98113
Objects.requireNonNull(message, "Message cannot be null");
99114
return switch (message) {
100115
case BungeeRequest request -> writeRequest(request);
@@ -110,8 +125,10 @@ static boolean isIdentifier(@Nullable String channel) {
110125
*
111126
* @param request the request to write
112127
* @return the byte array containing the serialized request
128+
* @throws NullPointerException if {@code request} is null
113129
*/
114-
static byte @NotNull [] writeRequest(@NotNull BungeeRequest request) {
130+
@Contract(pure = true)
131+
static byte[] writeRequest(BungeeRequest request) {
115132
Objects.requireNonNull(request, "Request cannot be null");
116133
return NetworkBuffer.makeArray(BungeeRequest.SERIALIZER, request);
117134
}
@@ -122,9 +139,11 @@ static boolean isIdentifier(@Nullable String channel) {
122139
*
123140
* @param bytes the byte array to read the request from
124141
* @return the request, never null
142+
* @throws NullPointerException if {@code bytes} is null
125143
* @throws IllegalStateException if there are leftover bytes in the buffer after reading the request
126144
*/
127-
static @NotNull BungeeRequest readRequest(byte @NotNull [] bytes) throws IllegalStateException {
145+
@Contract(pure = true)
146+
static BungeeRequest readRequest(byte[] bytes) throws IllegalStateException {
128147
Objects.requireNonNull(bytes, "Bytes cannot be null!");
129148
return readRequest(NetworkBuffer.wrap(bytes, 0, bytes.length));
130149
}
@@ -135,9 +154,11 @@ static boolean isIdentifier(@Nullable String channel) {
135154
*
136155
* @param buffer the buffer to read the response from
137156
* @return the response, never null
157+
* @throws NullPointerException if {@code buffer} is null
138158
* @throws IllegalStateException if there are leftover bytes in the buffer after reading the request
139159
*/
140-
static @NotNull BungeeRequest readRequest(@NotNull NetworkBuffer buffer) throws IllegalStateException {
160+
@Contract(mutates = "param1")
161+
static BungeeRequest readRequest(NetworkBuffer buffer) throws IllegalStateException {
141162
Objects.requireNonNull(buffer, "Buffer cannot be null!");
142163
return BungeeProtocol.read(buffer, BungeeRequest.SERIALIZER);
143164
}
@@ -149,8 +170,10 @@ static boolean isIdentifier(@Nullable String channel) {
149170
*
150171
* @param response the response to write
151172
* @return the byte array containing the serialized response
173+
* @throws NullPointerException if {@code response} is null
152174
*/
153-
static byte @NotNull [] writeResponse(@NotNull BungeeResponse response) {
175+
@Contract(pure = true)
176+
static byte[] writeResponse(BungeeResponse response) {
154177
Objects.requireNonNull(response, "Response cannot be null");
155178
return NetworkBuffer.makeArray(BungeeResponse.SERIALIZER, response);
156179
}
@@ -160,9 +183,11 @@ static boolean isIdentifier(@Nullable String channel) {
160183
*
161184
* @param bytes the byte array to read the response from
162185
* @return the response, never null
186+
* @throws NullPointerException if {@code bytes} is null
163187
* @throws IllegalStateException if there are leftover bytes in the buffer after reading the request
164188
*/
165-
static @NotNull BungeeResponse readResponse(byte @NotNull [] bytes) throws IllegalStateException {
189+
@Contract(pure = true)
190+
static BungeeResponse readResponse(byte[] bytes) throws IllegalStateException {
166191
Objects.requireNonNull(bytes, "Bytes cannot be null!");
167192
return readResponse(NetworkBuffer.wrap(bytes, 0, bytes.length));
168193
}
@@ -173,9 +198,11 @@ static boolean isIdentifier(@Nullable String channel) {
173198
*
174199
* @param buffer the buffer to read the response from
175200
* @return the response, never null
201+
* @throws NullPointerException if {@code buffer} is null
176202
* @throws IllegalStateException if there are leftover bytes in the buffer after reading the request
177203
*/
178-
static @NotNull BungeeResponse readResponse(@NotNull NetworkBuffer buffer) throws IllegalStateException {
204+
@Contract(mutates = "param1")
205+
static BungeeResponse readResponse(NetworkBuffer buffer) throws IllegalStateException {
179206
Objects.requireNonNull(buffer, "Buffer cannot be null!");
180207
return BungeeProtocol.read(buffer, BungeeResponse.SERIALIZER);
181208
}
@@ -188,9 +215,11 @@ static boolean isIdentifier(@Nullable String channel) {
188215
*
189216
* @param event the event to read the response from
190217
* @return the response, or null if the event is not a BungeeCord message
218+
* @throws NullPointerException if {@code event} is null
191219
* @throws IllegalStateException if there are leftover bytes in the buffer after reading the request
192220
*/
193-
static @Nullable BungeeResponse readResponse(@NotNull PlayerPluginMessageEvent event) throws IllegalStateException {
221+
@Contract(pure = true)
222+
static @Nullable BungeeResponse readResponse(PlayerPluginMessageEvent event) throws IllegalStateException {
194223
Objects.requireNonNull(event, "Event cannot be null");
195224
if (!isIdentifier(event.getIdentifier())) return null;
196225
return readResponse(event.getMessage());
@@ -205,8 +234,10 @@ static boolean isIdentifier(@Nullable String channel) {
205234
*
206235
* @param connection the player connection to send the message to
207236
* @param message the message to send
237+
* @throws NullPointerException if {@code connection} is null
238+
* @throws NullPointerException if {@code message} is null
208239
*/
209-
static void send(@NotNull PlayerConnection connection, @NotNull BungeeMessage message) {
240+
static void send(PlayerConnection connection, BungeeMessage message) {
210241
Objects.requireNonNull(connection, "Connection cannot be null");
211242
Objects.requireNonNull(message, "Message cannot be null");
212243
connection.sendPacket(message.toPacket());
@@ -220,8 +251,10 @@ static void send(@NotNull PlayerConnection connection, @NotNull BungeeMessage me
220251
*
221252
* @param audience the audience to send the message
222253
* @param message the message to send
254+
* @throws NullPointerException if {@code connection} is null
255+
* @throws NullPointerException if {@code message} is null
223256
*/
224-
static void send(@NotNull Audience audience, @NotNull BungeeMessage message) {
257+
static void send(Audience audience, BungeeMessage message) {
225258
Objects.requireNonNull(audience, "Audience cannot be null");
226259
Objects.requireNonNull(message, "Message cannot be null");
227260
PacketSendingUtils.sendPacket(audience, message.toPacket());
@@ -230,12 +263,16 @@ static void send(@NotNull Audience audience, @NotNull BungeeMessage message) {
230263
/**
231264
* Sends the message to a single audience from a collection of audiences.
232265
* <p>Shuffles the collection and sends the message to a random audience.</p>
266+
* <br>
267+
* Inherent's any side effects of {@link BungeeMessage#send(Audience, BungeeMessage)}
233268
*
234269
* @param audiences the collection of audiences to send the message to
235270
* @param message the message to send
271+
* @throws NullPointerException if {@code audiences} is null
272+
* @throws NullPointerException if {@code message} is null
236273
* @throws IllegalArgumentException if the collection is empty
237274
*/
238-
static void sendSingle(@NotNull Collection<? extends Audience> audiences, @NotNull BungeeMessage message) {
275+
static void sendSingle(Collection<? extends Audience> audiences, BungeeMessage message) {
239276
Objects.requireNonNull(audiences, "Audiences cannot be null");
240277
Objects.requireNonNull(message, "Message cannot be null");
241278
if (audiences.isEmpty()) throw new IllegalArgumentException("Audiences cannot be empty");
@@ -252,8 +289,9 @@ static void sendSingle(@NotNull Collection<? extends Audience> audiences, @NotNu
252289
*
253290
* @return the client plugin message packet containing the serialized message
254291
*/
255-
default @NotNull ClientPluginMessagePacket toClientPacket() {
256-
return new ClientPluginMessagePacket(BungeeProtocol.CHANNEL, write(this));
292+
@Contract(pure = true)
293+
default ClientPluginMessagePacket toClientPacket() {
294+
return new ClientPluginMessagePacket(BungeeProtocol.CHANNEL_LEGACY, write(this));
257295
}
258296

259297
/**
@@ -264,8 +302,9 @@ static void sendSingle(@NotNull Collection<? extends Audience> audiences, @NotNu
264302
*
265303
* @return the plugin message packet containing the serialized message
266304
*/
267-
default @NotNull PluginMessagePacket toPacket() {
268-
return new PluginMessagePacket(BungeeProtocol.CHANNEL, write(this));
305+
@Contract(pure = true)
306+
default PluginMessagePacket toPacket() {
307+
return new PluginMessagePacket(BungeeProtocol.CHANNEL_LEGACY, write(this));
269308
}
270309

271310
/**
@@ -276,8 +315,9 @@ static void sendSingle(@NotNull Collection<? extends Audience> audiences, @NotNu
276315
* </p>
277316
*
278317
* @param connection the player connection to send the message to
318+
* @throws NullPointerException if {@code connection} is null
279319
*/
280-
default void send(@NotNull PlayerConnection connection) {
320+
default void send(PlayerConnection connection) {
281321
Objects.requireNonNull(connection, "Connection cannot be null");
282322
BungeeMessage.send(connection, this);
283323
}
@@ -289,8 +329,9 @@ default void send(@NotNull PlayerConnection connection) {
289329
* </p>
290330
*
291331
* @param audience the audience to send the message
332+
* @throws NullPointerException if {@code audience} is null
292333
*/
293-
default void send(@NotNull Audience audience) {
334+
default void send(Audience audience) {
294335
Objects.requireNonNull(audience, "Audience cannot be null");
295336
BungeeMessage.send(audience, this);
296337
}
@@ -302,9 +343,10 @@ default void send(@NotNull Audience audience) {
302343
* </p>
303344
*
304345
* @param audiences the collection of audiences to send the message to
346+
* @throws NullPointerException if {@code audiences} is null
305347
* @throws IllegalArgumentException if the collection is empty
306348
*/
307-
default void sendSingle(@NotNull Collection<? extends Audience> audiences) {
349+
default void sendSingle(Collection<? extends Audience> audiences) {
308350
Objects.requireNonNull(audiences, "Audience cannot be null");
309351
if (audiences.isEmpty()) throw new IllegalArgumentException("Audiences cannot be empty");
310352
BungeeMessage.sendSingle(audiences, this);

src/main/java/dev/kerman/freight/BungeeProtocol.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import net.minestom.server.network.NetworkBuffer;
44
import net.minestom.server.network.NetworkBufferTemplate;
5-
import org.jetbrains.annotations.NotNull;
65
import org.jetbrains.annotations.Nullable;
76

87
import java.util.List;
8+
import java.util.Objects;
99
import java.util.UUID;
1010

1111
/**
@@ -17,7 +17,10 @@
1717
* The protocol is used to communicate between the server and BungeeCord proxy.
1818
*/
1919
final class BungeeProtocol {
20-
static final String CHANNEL = "BungeeCord"; // aka bungeecord:main
20+
static final String ALL = "ALL";
21+
static final String ONLINE = "ONLINE";
22+
static final String CHANNEL_MODERN = "bungeecord:main";
23+
static final String CHANNEL_LEGACY = "BungeeCord";
2124
static final NetworkBuffer.Type<List<String>> CSV_TYPE = NetworkBuffer.STRING_IO_UTF8.transform(
2225
string -> List.of(string.split(",")),
2326
stringList -> String.join(",", stringList)
@@ -31,15 +34,15 @@ final class BungeeProtocol {
3134
static final NetworkBuffer.Type<byte[]> SHORT_BYTE_ARRAY_TYPE = new NetworkBuffer.Type<>() {
3235
// Reminder that they use big endian for IO, so we should be good as the protocol uses it too
3336
@Override
34-
public void write(@NotNull NetworkBuffer buffer, byte @NotNull [] value) {
37+
public void write(NetworkBuffer buffer, byte[] value) {
3538
final int length = value.length;
3639
if (length > 65535) throw new IllegalStateException("Value too long");
3740
buffer.write(NetworkBuffer.UNSIGNED_SHORT, length);
3841
buffer.write(NetworkBuffer.FixedRawBytes(length), value);
3942
}
4043

4144
@Override
42-
public byte[] read(@NotNull NetworkBuffer buffer) {
45+
public byte[] read(NetworkBuffer buffer) {
4346
final int length = buffer.read(NetworkBuffer.UNSIGNED_SHORT);
4447
if (length > 65535) throw new IllegalStateException("Value too long");
4548
if (buffer.readableBytes() > length) throw new IllegalStateException("Value too long to read");
@@ -48,11 +51,11 @@ public byte[] read(@NotNull NetworkBuffer buffer) {
4851
};
4952

5053
static boolean isIdentifier(@Nullable String channel) {
51-
return CHANNEL.equals(channel) || "bungeecord:main".equals(channel);
54+
return CHANNEL_LEGACY.equals(channel) || CHANNEL_MODERN.equals(channel);
5255
}
5356

5457
// Reads the message from the buffer and checks if there are any leftover bytes
55-
static <T extends BungeeMessage> T read(@NotNull NetworkBuffer buffer, NetworkBuffer.@NotNull Type<T> type) throws IllegalStateException {
58+
static <T extends BungeeMessage> T read(NetworkBuffer buffer, NetworkBuffer.Type<T> type) throws IllegalStateException {
5659
final T read = buffer.read(type);
5760
final long readableBytes = buffer.readableBytes();
5861
if (readableBytes > 0) throw new IllegalStateException("%s message not fully read! %d bytes left over.".formatted(read.getClass().getName(), readableBytes));
@@ -89,13 +92,13 @@ enum Type {
8992
// Sometimes unprefixed, so we need to handle that.
9093
public static final NetworkBuffer.Type<Type> RESPONSE_SERIALIZER = new NetworkBuffer.Type<>() {
9194
@Override
92-
public void write(@NotNull NetworkBuffer buffer, Type value) {
95+
public void write(NetworkBuffer buffer, Type value) {
9396
if (value == Forward || value == ForwardToPlayer) return; // These are unprefixed, so we don't write the name.
9497
buffer.write(NetworkBuffer.STRING_IO_UTF8, value.name());
9598
}
9699

97100
@Override
98-
public Type read(@NotNull NetworkBuffer buffer) {
101+
public Type read(NetworkBuffer buffer) {
99102
final long readIndex = buffer.readIndex();
100103
try { // Try to determine if the type is prefixed or not.
101104
final String name = buffer.read(NetworkBuffer.STRING_IO_UTF8);
@@ -108,16 +111,16 @@ public Type read(@NotNull NetworkBuffer buffer) {
108111
};
109112

110113
private final NetworkBuffer.Type<? extends BungeeRequest> requestSerializer;
111-
private final NetworkBuffer.Type<? extends BungeeResponse> responseSerializer;
114+
private final NetworkBuffer.@Nullable Type<? extends BungeeResponse> responseSerializer;
112115

113-
Type(NetworkBuffer.Type<? extends BungeeRequest> requestSerializer, NetworkBuffer.Type<? extends BungeeResponse> responseSerializer) {
116+
Type(NetworkBuffer.Type<? extends BungeeRequest> requestSerializer, NetworkBuffer.@Nullable Type<? extends BungeeResponse> responseSerializer) {
114117
this.requestSerializer = requestSerializer;
115118
this.responseSerializer = responseSerializer;
116119
}
117120

118121
// Could probably use polymorphism here, but it makes the classes have less information about the serialization
119122
// See the entries in the regular ClientPacket and ServerPacket not caring about the id near the data structure.
120-
static @NotNull Type toType(@NotNull BungeeMessage message) {
123+
static Type toType(BungeeMessage message) {
121124
return switch (message) {
122125
// Requests
123126
case BungeeRequest.Connect ignored -> Connect;
@@ -154,7 +157,7 @@ public Type read(@NotNull NetworkBuffer buffer) {
154157
}
155158

156159
public NetworkBuffer.Type<? extends BungeeResponse> responseSerializer() {
157-
return responseSerializer;
160+
return Objects.requireNonNull(responseSerializer, "Response serializer must not be null");
158161
}
159162

160163
public NetworkBuffer.Type<? extends BungeeRequest> requestSerializer() {

0 commit comments

Comments
 (0)