Skip to content

Commit d8eb06f

Browse files
committed
[networking] improve handling of legacy channel format
1 parent 36bfdb5 commit d8eb06f

5 files changed

Lines changed: 142 additions & 36 deletions

File tree

‎libraries/networking/README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ and snake case for the identifier.
2727
public static final NamespacedIdentifier COOKIE_CHANNEL = ChannelIdentifiers.from("example", "cookie");
2828
```
2929

30+
In 1.13-pre2 and below, the game did not validate channel names, so Minecraft itself used names like `MC|Brand`. If you require a channel name with this convention, you can use the `fromLegacy` factory methods instead.
31+
32+
```java
33+
ChannelIdentifiers.from("example", "cookie"); // -> "example:cookie"
34+
ChannelIdentifiers.fromLegacy("EXAMPLE", "Cookie"); // -> "EXAMPLE|Cookie"
35+
```
36+
3037
You are expected to register your channels through the `ChannelRegistry`.
3138

3239
```java

‎libraries/networking/src/main/java/net/ornithemc/osl/networking/api/ChannelIdentifiers.java‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import net.ornithemc.osl.core.api.util.NamespacedIdentifiers;
88
import net.ornithemc.osl.core.impl.util.NamespacedIdentifierException;
99
import net.ornithemc.osl.networking.impl.ChannelIdentifierException;
10+
import net.ornithemc.osl.networking.impl.LegacyChannelIdentifierImpl;
1011

1112
/**
1213
* Utility methods for creating and validating channel identifiers.
@@ -45,6 +46,24 @@ public static NamespacedIdentifier from(String namespace, String identifier) {
4546
);
4647
}
4748

49+
/**
50+
* Construct a legacy channel identifier with the given identifier.
51+
*
52+
* @see LegacyChannelIdentifier
53+
*/
54+
public static LegacyChannelIdentifier fromLegacy(String identifier) {
55+
return fromLegacy("", identifier);
56+
}
57+
58+
/**
59+
* Construct a legacy channel identifier with the given namespace and identifier.
60+
*
61+
* @see LegacyChannelIdentifier
62+
*/
63+
public static LegacyChannelIdentifier fromLegacy(String namespace, String identifier) {
64+
return new LegacyChannelIdentifierImpl(namespace, identifier);
65+
}
66+
4867
/**
4968
* Check whether the given channel identifier is valid, or throw an exception.
5069
*/
@@ -54,7 +73,7 @@ public static NamespacedIdentifier validate(NamespacedIdentifier id) {
5473
validateIdentifier(id.identifier());
5574

5675
return id;
57-
} catch (ChannelIdentifierException e) {
76+
} catch (ChannelIdentifierException | NamespacedIdentifierException e) {
5877
throw ChannelIdentifierException.invalid(id, e);
5978
}
6079
}
@@ -63,15 +82,9 @@ public static NamespacedIdentifier validate(NamespacedIdentifier id) {
6382
* Check that the given namespace is valid for a channel identifier.
6483
*/
6584
public static String validateNamespace(String namespace) {
66-
if (namespace == null || namespace.isEmpty()) {
67-
throw ChannelIdentifierException.invalidNamespace(namespace, "null or empty");
68-
}
6985
if (namespace.length() > MAX_LENGTH_NAMESPACE) {
7086
throw ChannelIdentifierException.invalidNamespace(namespace, "length " + namespace.length() + " is greater than maximum allowed " + MAX_LENGTH_NAMESPACE);
7187
}
72-
if (!namespace.chars().allMatch(chr -> chr == '-' || chr == '.' || chr == '_' || (chr >= 'a' && chr <= 'z') || (chr >= '0' && chr <= '9'))) {
73-
throw ChannelIdentifierException.invalidNamespace(namespace, "contains illegal characters - only [a-z0-9-._] are allowed");
74-
}
7588

7689
return NamespacedIdentifiers.validateNamespace(namespace);
7790
}
@@ -80,15 +93,9 @@ public static String validateNamespace(String namespace) {
8093
* Check that the given identifier is valid for a channel identifier.
8194
*/
8295
public static String validateIdentifier(String identifier) {
83-
if (identifier == null || identifier.isEmpty()) {
84-
throw ChannelIdentifierException.invalidIdentifier(identifier, "null or empty");
85-
}
8696
if (identifier.length() > MAX_LENGTH_IDENTIFIER) {
8797
throw ChannelIdentifierException.invalidIdentifier(identifier, "length " + identifier.length() + " is greater than maximum allowed " + MAX_LENGTH_IDENTIFIER);
8898
}
89-
if (!identifier.chars().allMatch(chr -> chr == '-' || chr == '.' || chr == '_' || chr == '/' || (chr >= 'a' && chr <= 'z') || (chr >= '0' && chr <= '9'))) {
90-
throw ChannelIdentifierException.invalidIdentifier(identifier, "contains illegal characters - only [a-z0-9-._/] are allowed");
91-
}
9299

93100
return NamespacedIdentifiers.validateIdentifier(identifier);
94101
}
@@ -99,7 +106,7 @@ public static Set<NamespacedIdentifier> dropInvalid(Set<NamespacedIdentifier> ch
99106
.filter(channel -> {
100107
try {
101108
return ChannelIdentifiers.validate(channel) != null;
102-
} catch (ChannelIdentifierException | NamespacedIdentifierException e) {
109+
} catch (ChannelIdentifierException e) {
103110
return false;
104111
}
105112
})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package net.ornithemc.osl.networking.api;
2+
3+
import net.ornithemc.osl.core.api.util.NamespacedIdentifier;
4+
5+
/**
6+
* A {@linkplain NamespacedIdentifier} implementation that represents a packet
7+
* channel in the legacy format used in 1.13-pre2 and below. The legacy format
8+
* uses the vertical bar character ({@code '|'}) as the separator, but that is
9+
* only by convention, as no format is strictly enforced, and any character in
10+
* the UTF-8 character set is allowed. As such, strings like {@code "MC|Brand"}
11+
* and {@code "REGISTER"} are valid channel identifiers in the legacy format.
12+
*/
13+
public interface LegacyChannelIdentifier extends NamespacedIdentifier {
14+
15+
char SEPARATOR = '|';
16+
17+
@Override
18+
LegacyChannelIdentifier prefixed(String prefix);
19+
20+
@Override
21+
LegacyChannelIdentifier suffixed(String suffix);
22+
23+
}

‎libraries/networking/src/main/java/net/ornithemc/osl/networking/api/StringChannelIdentifierParser.java‎

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import net.ornithemc.osl.core.api.util.NamespacedIdentifier;
44
import net.ornithemc.osl.core.impl.util.NamespacedIdentifierImpl;
5-
import net.ornithemc.osl.networking.impl.ChannelIdentifierException;
65
import net.ornithemc.osl.networking.impl.ChannelIdentifierParseException;
6+
import net.ornithemc.osl.networking.impl.LegacyChannelIdentifierImpl;
77

88
/**
99
* Utility methods for converting channel identifiers from and to {@link String}s.
@@ -23,15 +23,20 @@ public final class StringChannelIdentifierParser {
2323
* which may or may not be a valid identifier.
2424
*/
2525
public static NamespacedIdentifier fromString(String s) {
26-
int i = s.indexOf('|');
26+
int i = s.indexOf(LegacyChannelIdentifier.SEPARATOR);
2727

28-
if (i < 1) {
29-
// allow null namespaces to support channel ids that do not conform
30-
// to OSL spec - MC did not enforce a strict spec before 1.13
31-
return new NamespacedIdentifierImpl("", s);
32-
} else {
33-
return new NamespacedIdentifierImpl(s.substring(0, i), s.substring(i + 1));
28+
if (i > 0) {
29+
return new LegacyChannelIdentifierImpl(s.substring(0, i), s.substring(i + 1));
3430
}
31+
32+
i = s.indexOf(NamespacedIdentifier.SEPARATOR);
33+
34+
if (i < 0) {
35+
// allow empty namespaces since MC did not enforce a strict spec before 1.13
36+
return new LegacyChannelIdentifierImpl("", s);
37+
}
38+
39+
return new NamespacedIdentifierImpl(s.substring(0, i), s.substring(i + 1));
3540
}
3641

3742
/**
@@ -45,27 +50,26 @@ public static NamespacedIdentifier fromString(String s) {
4550
* if no valid channel identifier can be parsed from the given {@code String}.
4651
*/
4752
public static NamespacedIdentifier fromStringOrThrow(String s) {
48-
int i = s.indexOf('|');
53+
int i = s.indexOf(LegacyChannelIdentifier.SEPARATOR);
4954

50-
try {
51-
if (i < 0) {
52-
return ChannelIdentifiers.from(s);
53-
} else if (i > 0) {
54-
return ChannelIdentifiers.from(s.substring(0, i), s.substring(i + 1));
55-
} else {
56-
throw ChannelIdentifierParseException.invalid(s, "badly formatted");
57-
}
58-
} catch (ChannelIdentifierException e) {
59-
throw ChannelIdentifierParseException.invalid(s, e);
55+
if (i > 0) {
56+
return new LegacyChannelIdentifierImpl(s.substring(0, i), s.substring(i + 1));
6057
}
58+
59+
i = s.indexOf(NamespacedIdentifier.SEPARATOR);
60+
61+
if (i < 0) {
62+
// allow empty namespaces since MC did not enforce a strict spec before 1.13
63+
return new LegacyChannelIdentifierImpl("", s);
64+
}
65+
66+
return ChannelIdentifiers.from(s.substring(0, i), s.substring(i + 1));
6167
}
6268

6369
/**
6470
* Convert the given {@code NamespacedIdentifier} to its {@code String} representation.
6571
*/
6672
public static String toString(NamespacedIdentifier id) {
67-
return id.namespace().isEmpty()
68-
? id.identifier()
69-
: id.namespace() + "|" + id.identifier();
73+
return id.toString();
7074
}
7175
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package net.ornithemc.osl.networking.impl;
2+
3+
import net.ornithemc.osl.core.api.util.NamespacedIdentifier;
4+
import net.ornithemc.osl.core.api.util.NamespacedIdentifiers;
5+
import net.ornithemc.osl.networking.api.LegacyChannelIdentifier;
6+
7+
/**
8+
* A {@linkplain NamespacedIdentifier} implementation that represents a packet
9+
* channel in the legacy format used in 1.13-pre2 and below. The legacy format
10+
* uses the vertical bar character ({@code '|'}) as the separator, but that is
11+
* only by convention, as no format is strictly enforced, and any character in
12+
* the UTF-8 character set is allowed. As such, strings like {@code "MC|Brand"}
13+
* and {@code "REGISTER"} are valid channel identifiers in the legacy format.
14+
*/
15+
public final class LegacyChannelIdentifierImpl implements LegacyChannelIdentifier {
16+
17+
private final String namespace;
18+
private final String identifier;
19+
20+
public LegacyChannelIdentifierImpl(String namespace, String identifier) {
21+
this.namespace = namespace;
22+
this.identifier = identifier;
23+
}
24+
25+
@Override
26+
public boolean equals(Object o) {
27+
if (this == o) {
28+
return true;
29+
}
30+
if (!(o instanceof LegacyChannelIdentifier)) {
31+
return false;
32+
}
33+
return NamespacedIdentifiers.equals(this, (LegacyChannelIdentifier) o);
34+
}
35+
36+
@Override
37+
public int hashCode() {
38+
return 31 * namespace.hashCode() + identifier.hashCode();
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return namespace.isEmpty() ? identifier : (namespace + SEPARATOR + identifier);
44+
}
45+
46+
@Override
47+
public String namespace() {
48+
return namespace;
49+
}
50+
51+
@Override
52+
public String identifier() {
53+
return identifier;
54+
}
55+
56+
@Override
57+
public LegacyChannelIdentifier prefixed(String prefix) {
58+
return new LegacyChannelIdentifierImpl(namespace, prefix + identifier);
59+
}
60+
61+
@Override
62+
public LegacyChannelIdentifier suffixed(String suffix) {
63+
return new LegacyChannelIdentifierImpl(namespace, identifier + suffix);
64+
}
65+
}

0 commit comments

Comments
 (0)