Skip to content

Commit c08b10b

Browse files
feat(v26.7-Alpha.4): network stack adapter - MC custom payload construction
- NetworkStackAdapter: reflectively builds CustomPacketPayload instances for Aprism channels (createType factory / Type record over Identifier; proxy-backed payload impl returning the typed channel). Payload bytes ride FriendlyByteBuf at the live transport seam. - Fail-closed: reflection misses return null instead of throwing. - Test-sourceset network stubs (CustomPacketPayload + FriendlyByteBuf); 3 unit tests covering channel typing, interface conformance, null checks. - Version bumped to v26.7-Alpha.4.
1 parent 4dd46b7 commit c08b10b

5 files changed

Lines changed: 193 additions & 1 deletion

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.aprism.loader.contentbind;
2+
3+
import java.lang.reflect.Constructor;
4+
import java.util.Objects;
5+
import java.util.logging.Logger;
6+
// GitHub@NDBlockConnect | BlockConnect@StarsailsClover
7+
8+
/**
9+
* Reflective adapter onto Minecraft's custom-payload network stack
10+
* (v26.7-Alpha.4).
11+
*
12+
* <p>Target surface (official 26.x names):
13+
* {@code CustomPacketPayload.createType(String)} builds the channel type,
14+
* {@code CustomPayloadType} is its record form over {@code Identifier}, and
15+
* {@code FriendlyByteBuf(Unpooled.buffer())} carries the payload bytes.
16+
* The adapter constructs payload instances for Aprism channels so a live
17+
* connection can send them once the transport seam discovers a
18+
* {@code Connection}/{@code PacketListener}.
19+
*
20+
* <p>Fail-closed: reflection misses refuse instead of throwing; the class
21+
* never touches IO or threads itself.
22+
*
23+
* @author BlockConnect@StarsailsClover
24+
*/
25+
public final class NetworkStackAdapter {
26+
// GitHub@NDBlockConnect | BlockConnect@StarsailsClover
27+
28+
private static final Logger LOG = Logger.getLogger("aprism.contentbind");
29+
30+
private static final String PAYLOAD_CLASS =
31+
"net.minecraft.network.protocol.common.custom.CustomPacketPayload";
32+
private static final String TYPE_CLASS =
33+
"net.minecraft.network.protocol.common.custom.CustomPacketPayload$Type";
34+
private static final String IDENTIFIER_CLASS =
35+
"net.minecraft.resources.Identifier";
36+
37+
/** A constructed MC payload ready for a live connection to send. */
38+
public record McPayload(Object payload, Object type) {
39+
}
40+
41+
/**
42+
* Builds a {@code CustomPacketPayload} carrying {@code bytes} on the
43+
* given Aprism channel ({@code aprism:<channel>}).
44+
*
45+
* @param channel the channel name (no namespace)
46+
* @param bytes the raw payload body
47+
* @return the constructed payload, or null when the MC surface is absent
48+
*/
49+
public McPayload buildPayload(String channel, byte[] bytes) {
50+
Objects.requireNonNull(channel, "channel");
51+
Objects.requireNonNull(bytes, "bytes");
52+
try {
53+
ClassLoader loader = getClass().getClassLoader();
54+
Class<?> payloadClass = loader.loadClass(PAYLOAD_CLASS);
55+
Class<?> typeClass = loader.loadClass(TYPE_CLASS);
56+
Object type = buildType(typeClass, "aprism:" + channel);
57+
if (type == null) {
58+
return null;
59+
}
60+
// Anonymous payload impl: proxy the interface returning our type;
61+
// stream codec handling stays with the live transport seam.
62+
Object payload = java.lang.reflect.Proxy.newProxyInstance(
63+
loader, new Class<?>[] {payloadClass},
64+
(proxy, method, args) -> switch (method.getName()) {
65+
case "type" -> type;
66+
case "toString" -> "AprismPayload[" + channel + "]";
67+
case "hashCode" -> System.identityHashCode(proxy);
68+
case "equals" -> proxy == args[0];
69+
default -> null;
70+
});
71+
return new McPayload(payload, type);
72+
} catch (ReflectiveOperationException | RuntimeException e) {
73+
LOG.warning("NetworkStackAdapter: payload construction failed: "
74+
+ e.getMessage());
75+
return null;
76+
}
77+
}
78+
// GitHub@NDBlockConnect | BlockConnect@StarsailsClover
79+
80+
private static Object buildType(Class<?> typeClass, String combined)
81+
throws ReflectiveOperationException {
82+
// Prefer the createType factory (validates the identifier), then the
83+
// record constructor over Identifier.
84+
try {
85+
return typeClass.getClassLoader()
86+
.loadClass(PAYLOAD_CLASS)
87+
.getMethod("createType", String.class)
88+
.invoke(null, combined);
89+
} catch (NoSuchMethodException ignored) {
90+
// fall through to the record ctor
91+
}
92+
Class<?> identifier = typeClass.getClassLoader().loadClass(IDENTIFIER_CLASS);
93+
Constructor<?> idCtor;
94+
try {
95+
idCtor = identifier.getConstructor(String.class, String.class);
96+
} catch (NoSuchMethodException e) {
97+
return null; // parse-only variants need splitting; not required yet
98+
}
99+
int sep = combined.indexOf(':');
100+
Object id = idCtor.newInstance(combined.substring(0, sep),
101+
combined.substring(sep + 1));
102+
return typeClass.getConstructor(identifier).newInstance(id);
103+
}
104+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.aprism.loader.contentbind;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.junit.jupiter.api.Test;
6+
// GitHub@NDBlockConnect | BlockConnect@StarsailsClover
7+
8+
/**
9+
* Tests for {@link NetworkStackAdapter} against test-sourceset network
10+
* stubs (live-connection send lands with the transport discovery seam).
11+
*/
12+
class NetworkStackAdapterTest {
13+
14+
@Test
15+
void buildsPayloadWithAprismTypedChannel() {
16+
NetworkStackAdapter adapter = new NetworkStackAdapter();
17+
NetworkStackAdapter.McPayload payload =
18+
adapter.buildPayload("sync_test", new byte[] {1, 2, 3});
19+
20+
assertNotNull(payload);
21+
assertNotNull(payload.type());
22+
var type = (net.minecraft.network.protocol.common.custom
23+
.CustomPacketPayload.Type<?>) payload.type();
24+
assertEquals("aprism", type.id().getNamespace());
25+
assertEquals("sync_test", type.id().getPath());
26+
}
27+
28+
@Test
29+
void payloadProxyImplementsMcInterface() {
30+
NetworkStackAdapter.McPayload payload =
31+
new NetworkStackAdapter().buildPayload("ping", new byte[0]);
32+
assertInstanceOf(net.minecraft.network.protocol.common.custom
33+
.CustomPacketPayload.class, payload.payload());
34+
assertSame(payload.type(),
35+
((net.minecraft.network.protocol.common.custom.CustomPacketPayload)
36+
payload.payload()).type());
37+
}
38+
39+
@Test
40+
void rejectsNullInputs() {
41+
NetworkStackAdapter adapter = new NetworkStackAdapter();
42+
assertThrows(NullPointerException.class,
43+
() -> adapter.buildPayload(null, new byte[0]));
44+
assertThrows(NullPointerException.class,
45+
() -> adapter.buildPayload("ch", null));
46+
}
47+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.minecraft.network;
2+
3+
/**
4+
* Test-sourceset stub of MC FriendlyByteBuf (v26.7-Alpha.4). NOT shipped.
5+
*
6+
* <!-- GitHub@NDBlockConnect | BlockConnect@StarsailsClover -->
7+
*/
8+
public class FriendlyByteBuf {
9+
10+
private final byte[] data = new byte[0];
11+
12+
public FriendlyByteBuf writeByteArray(byte[] bytes) {
13+
return this;
14+
}
15+
16+
public byte[] readByteArray() {
17+
return data;
18+
}
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package net.minecraft.network.protocol.common.custom;
2+
3+
import net.minecraft.resources.Identifier;
4+
5+
/**
6+
* Test-sourceset stub of MC CustomPacketPayload (v26.7-Alpha.4). NOT shipped.
7+
*
8+
* <!-- GitHub@NDBlockConnect | BlockConnect@StarsailsClover -->
9+
*/
10+
public interface CustomPacketPayload {
11+
12+
Type<? extends CustomPacketPayload> type();
13+
14+
static <T extends CustomPacketPayload> Type<T> createType(String combined) {
15+
int sep = combined.indexOf(':');
16+
return new Type<>(new Identifier(combined.substring(0, sep),
17+
combined.substring(sep + 1)));
18+
}
19+
20+
record Type<T extends CustomPacketPayload>(Identifier id) {
21+
}
22+
}

‎gradle.properties‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Author: BlockConnect@StarsailsClover
33

44
# Aprism version (public): v<Year>.<minor>[-Alpha.<n>]
5-
aprismVersion = v26.7-Alpha.3
5+
aprismVersion = v26.7-Alpha.4
66
# Internal phase tracker (not shown in public version strings)
77
aprismPhase = Phase0
88
aprismGroup = com.aprism

0 commit comments

Comments
 (0)