Skip to content

Commit 5fc19bb

Browse files
Implement event-driven packet system (#19)
1 parent acdca0d commit 5fc19bb

13 files changed

+254
-88
lines changed

src/main/java/net/hypixel/modapi/HypixelModAPI.java

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@
44
import net.hypixel.modapi.error.ModAPIException;
55
import net.hypixel.modapi.handler.ClientboundPacketHandler;
66
import net.hypixel.modapi.packet.ClientboundHypixelPacket;
7+
import net.hypixel.modapi.packet.EventPacket;
78
import net.hypixel.modapi.packet.HypixelPacket;
89
import net.hypixel.modapi.packet.PacketRegistry;
910
import net.hypixel.modapi.packet.impl.clientbound.*;
10-
import net.hypixel.modapi.packet.impl.serverbound.ServerboundLocationPacket;
11-
import net.hypixel.modapi.packet.impl.serverbound.ServerboundPartyInfoPacket;
12-
import net.hypixel.modapi.packet.impl.serverbound.ServerboundPingPacket;
13-
import net.hypixel.modapi.packet.impl.serverbound.ServerboundPlayerInfoPacket;
11+
import net.hypixel.modapi.packet.impl.clientbound.ClientboundHelloPacket;
12+
import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket;
13+
import net.hypixel.modapi.packet.impl.serverbound.*;
1414
import net.hypixel.modapi.serializer.PacketSerializer;
1515

16-
import java.util.List;
16+
import java.util.*;
17+
import java.util.concurrent.ConcurrentHashMap;
1718
import java.util.concurrent.CopyOnWriteArrayList;
18-
import java.util.function.Consumer;
19+
import java.util.function.Predicate;
1920

2021
public class HypixelModAPI {
2122
private static final HypixelModAPI INSTANCE = new HypixelModAPI();
@@ -26,25 +27,54 @@ public static HypixelModAPI getInstance() {
2627

2728
private final PacketRegistry registry = new PacketRegistry();
2829
private final List<ClientboundPacketHandler> handlers = new CopyOnWriteArrayList<>();
29-
private Consumer<HypixelPacket> packetSender = null;
30+
private final Set<String> subscribedEvents = ConcurrentHashMap.newKeySet();
31+
private Set<String> lastSubscribedEvents = Collections.emptySet();
32+
private Predicate<HypixelPacket> packetSender = null;
3033

3134
private HypixelModAPI() {
35+
registerHypixelPackets();
36+
registerEventPackets();
37+
registerDefaultHandler();
38+
}
39+
40+
private void registerHypixelPackets() {
41+
registry.define("hypixel:hello")
42+
.clientbound(ClientboundHelloPacket.class, ClientboundHelloPacket::new)
43+
.register();
44+
3245
registry.define("hypixel:ping")
3346
.clientbound(ClientboundPingPacket.class, ClientboundPingPacket::new)
3447
.serverbound(ServerboundPingPacket.class, ServerboundPingPacket::new)
3548
.register();
36-
registry.define("hypixel:location")
37-
.clientbound(ClientboundLocationPacket.class, ClientboundLocationPacket::new)
38-
.serverbound(ServerboundLocationPacket.class, ServerboundLocationPacket::new)
39-
.register();
49+
4050
registry.define("hypixel:party_info")
4151
.clientbound(ClientboundPartyInfoPacket.class, ClientboundPartyInfoPacket::new)
4252
.serverbound(ServerboundPartyInfoPacket.class, ServerboundPartyInfoPacket::new)
4353
.register();
54+
4455
registry.define("hypixel:player_info")
4556
.clientbound(ClientboundPlayerInfoPacket.class, ClientboundPlayerInfoPacket::new)
4657
.serverbound(ServerboundPlayerInfoPacket.class, ServerboundPlayerInfoPacket::new)
4758
.register();
59+
60+
registry.define("hypixel:register")
61+
.serverbound(ServerboundRegisterPacket.class, ServerboundRegisterPacket::new)
62+
.register();
63+
}
64+
65+
private void registerEventPackets() {
66+
registry.define("hyevent:location")
67+
.clientBoundEvent(ClientboundLocationPacket.CURRENT_VERSION, ClientboundLocationPacket.class, ClientboundLocationPacket::new)
68+
.register();
69+
}
70+
71+
private void registerDefaultHandler() {
72+
registerHandler(new ClientboundPacketHandler() {
73+
@Override
74+
public void onHelloEvent(ClientboundHelloPacket packet) {
75+
sendRegisterPacket(true);
76+
}
77+
});
4878
}
4979

5080
public PacketRegistry getRegistry() {
@@ -55,6 +85,24 @@ public void registerHandler(ClientboundPacketHandler handler) {
5585
handlers.add(handler);
5686
}
5787

88+
public void subscribeToEventPacket(Class<? extends EventPacket> packet) {
89+
if (subscribedEvents.add(getRegistry().getIdentifier(packet))) {
90+
sendRegisterPacket(false);
91+
}
92+
}
93+
94+
private void sendRegisterPacket(boolean alwaysSendIfNotEmpty) {
95+
if (lastSubscribedEvents.equals(subscribedEvents) && !(alwaysSendIfNotEmpty && !subscribedEvents.isEmpty())) {
96+
return;
97+
}
98+
99+
Set<String> lastSubscribedEvents = new HashSet<>(subscribedEvents);
100+
Map<String, Integer> versionsMap = getRegistry().getEventVersions(lastSubscribedEvents);
101+
if (sendPacket(new ServerboundRegisterPacket(versionsMap))) {
102+
this.lastSubscribedEvents = lastSubscribedEvents;
103+
}
104+
}
105+
58106
public void handle(String identifier, PacketSerializer serializer) {
59107
if (handlers.isEmpty()) {
60108
return;
@@ -85,17 +133,21 @@ public void handle(ClientboundHypixelPacket packet) {
85133
}
86134
}
87135

88-
public void setPacketSender(Consumer<HypixelPacket> packetSender) {
136+
public void setPacketSender(Predicate<HypixelPacket> packetSender) {
89137
if (this.packetSender != null) {
90138
throw new IllegalArgumentException("Packet sender already set");
91139
}
92140
this.packetSender = packetSender;
93141
}
94142

95-
public void sendPacket(HypixelPacket packet) {
143+
/**
144+
* @return whether the packet was sent successfully
145+
*/
146+
public boolean sendPacket(HypixelPacket packet) {
96147
if (packetSender == null) {
97148
throw new IllegalStateException("Packet sender not set");
98149
}
99-
packetSender.accept(packet);
150+
151+
return packetSender.test(packet);
100152
}
101153
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package net.hypixel.modapi.annotation;
2+
3+
/**
4+
* Marks a packet as experimental and subject to change or removal.
5+
*/
6+
public @interface Experimental {
7+
}
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
package net.hypixel.modapi.handler;
22

3-
import net.hypixel.modapi.packet.impl.clientbound.ClientboundLocationPacket;
3+
import net.hypixel.modapi.packet.impl.clientbound.ClientboundHelloPacket;
44
import net.hypixel.modapi.packet.impl.clientbound.ClientboundPartyInfoPacket;
55
import net.hypixel.modapi.packet.impl.clientbound.ClientboundPingPacket;
66
import net.hypixel.modapi.packet.impl.clientbound.ClientboundPlayerInfoPacket;
7+
import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket;
78

89
public interface ClientboundPacketHandler {
910

10-
default void onPingPacket(ClientboundPingPacket packet) {
11+
default void onHelloEvent(ClientboundHelloPacket packet) {
1112
}
1213

13-
default void onLocationPacket(ClientboundLocationPacket packet) {
14+
default void onPingPacket(ClientboundPingPacket packet) {
1415
}
1516

1617
default void onPartyInfoPacket(ClientboundPartyInfoPacket packet) {
1718
}
1819

1920
default void onPlayerInfoPacket(ClientboundPlayerInfoPacket packet) {
2021
}
22+
23+
default void onLocationEvent(ClientboundLocationPacket packet) {
24+
}
2125
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package net.hypixel.modapi.packet;
2+
3+
/**
4+
* Represents a packet that is automatically sent based automatically based on specific conditions.
5+
* <p>
6+
* Event driven packets do not have a serverbound instance, as they can only be triggered by the server.
7+
* You will however, need to subscribe to wanted events, see {@link net.hypixel.modapi.HypixelModAPI#subscribeToEventPacket(Class)}.
8+
*/
9+
public interface EventPacket extends ClientboundHypixelPacket {
10+
}

src/main/java/net/hypixel/modapi/packet/PacketRegistry.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ public class PacketRegistry {
1717

1818
private void register(
1919
String identifier,
20+
int version,
2021
Class<? extends ClientboundHypixelPacket> clientboundClazz, Function<PacketSerializer, ? extends ClientboundHypixelPacket> clientPacketFactory,
2122
Class<? extends HypixelPacket> serverboundClazz, Function<PacketSerializer, ? extends HypixelPacket> serverPacketFactory) {
22-
registrations.put(identifier, new Registration(clientboundClazz, clientPacketFactory, serverboundClazz, serverPacketFactory));
23+
registrations.put(identifier, new Registration(version, clientboundClazz, clientPacketFactory, serverboundClazz, serverPacketFactory));
2324
if (clientboundClazz != null) {
2425
classToIdentifier.put(clientboundClazz, identifier);
2526
}
@@ -90,6 +91,17 @@ public Set<String> getServerboundIdentifiers() {
9091
.collect(Collectors.toSet()));
9192
}
9293

94+
/**
95+
* @param wantedEventIdentifiers the identifiers of the event packets we want to get versions for
96+
* @return a map of the event identifiers to their versions, only includes event packets that are registered as event packets with known versions
97+
*/
98+
public Map<String, Integer> getEventVersions(Set<String> wantedEventIdentifiers) {
99+
return wantedEventIdentifiers.stream()
100+
.filter(this::isRegistered)
101+
.filter(identifier -> getRegistration(identifier).getVersion() > 0)
102+
.collect(Collectors.toMap(Function.identity(), identifier -> getRegistration(identifier).getVersion()));
103+
}
104+
93105
public static final class RegistrationBuilder {
94106
private final PacketRegistry registry;
95107
private final String identifier;
@@ -98,12 +110,21 @@ public static final class RegistrationBuilder {
98110
private Function<PacketSerializer, ? extends ClientboundHypixelPacket> clientPacketFactory;
99111
private Class<? extends HypixelPacket> serverboundClazz;
100112
private Function<PacketSerializer, ? extends HypixelPacket> serverPacketFactory;
113+
private int version = 0;
101114

102115
RegistrationBuilder(PacketRegistry registry, String identifier) {
103116
this.registry = registry;
104117
this.identifier = identifier;
105118
}
106119

120+
public <T extends ClientboundHypixelPacket> RegistrationBuilder clientBoundEvent(
121+
int version, Class<T> clientboundClazz, Function<PacketSerializer, T> clientPacketFactory) {
122+
this.version = version;
123+
this.clientboundClazz = clientboundClazz;
124+
this.clientPacketFactory = clientPacketFactory;
125+
return this;
126+
}
127+
107128
public <T extends ClientboundHypixelPacket> RegistrationBuilder clientbound(
108129
Class<T> clientboundClazz, Function<PacketSerializer, T> clientPacketFactory) {
109130
this.clientboundClazz = clientboundClazz;
@@ -119,26 +140,33 @@ public <T extends HypixelPacket> RegistrationBuilder serverbound(
119140
}
120141

121142
public void register() {
122-
registry.register(identifier, clientboundClazz, clientPacketFactory, serverboundClazz, serverPacketFactory);
143+
registry.register(identifier, version, clientboundClazz, clientPacketFactory, serverboundClazz, serverPacketFactory);
123144
}
124145

125146
}
126147

127148
static final class Registration {
128149

150+
private final int version;
129151
private final Class<? extends ClientboundHypixelPacket> clientboundClazz;
130152
private final Function<PacketSerializer, ? extends ClientboundHypixelPacket> clientPacketFactory;
131153
private final Class<? extends HypixelPacket> serverboundClazz;
132154
private final Function<PacketSerializer, ? extends HypixelPacket> serverPacketFactory;
133155

134-
public Registration(Class<? extends ClientboundHypixelPacket> clientboundClazz, Function<PacketSerializer, ? extends ClientboundHypixelPacket> clientPacketFactory,
156+
public Registration(int version,
157+
Class<? extends ClientboundHypixelPacket> clientboundClazz, Function<PacketSerializer, ? extends ClientboundHypixelPacket> clientPacketFactory,
135158
Class<? extends HypixelPacket> serverboundClazz, Function<PacketSerializer, ? extends HypixelPacket> serverPacketFactory) {
159+
this.version = version;
136160
this.clientboundClazz = clientboundClazz;
137161
this.clientPacketFactory = clientPacketFactory;
138162
this.serverboundClazz = serverboundClazz;
139163
this.serverPacketFactory = serverPacketFactory;
140164
}
141165

166+
int getVersion() {
167+
return version;
168+
}
169+
142170
Class<? extends ClientboundHypixelPacket> getClientboundClazz() {
143171
return clientboundClazz;
144172
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package net.hypixel.modapi.packet.impl.clientbound;
2+
3+
import net.hypixel.data.region.Environment;
4+
import net.hypixel.modapi.annotation.Experimental;
5+
import net.hypixel.modapi.handler.ClientboundPacketHandler;
6+
import net.hypixel.modapi.packet.ClientboundHypixelPacket;
7+
import net.hypixel.modapi.serializer.PacketSerializer;
8+
9+
/**
10+
* This packet is automatically sent on every join to Hypixel to indicate that the client has connected to a Hypixel server.
11+
* Due to the nature of this packet, it is implemented without versioning and designed to discard extra bytes in the case that more data is added in the future.
12+
*/
13+
public class ClientboundHelloPacket implements ClientboundHypixelPacket {
14+
private final Environment environment;
15+
16+
public ClientboundHelloPacket(Environment environment) {
17+
this.environment = environment;
18+
}
19+
20+
public ClientboundHelloPacket(PacketSerializer serializer) {
21+
this.environment = Environment.getById(serializer.readVarInt()).orElseThrow(() -> new IllegalArgumentException("Invalid environment ID"));
22+
23+
// Read any remaining bytes, so that if more data is added in the future, it will be discarded on older clients
24+
serializer.discardRemaining();
25+
}
26+
27+
@Override
28+
public void handle(ClientboundPacketHandler handler) {
29+
handler.onHelloEvent(this);
30+
}
31+
32+
@Override
33+
public void write(PacketSerializer serializer) {
34+
serializer.writeVarInt(environment.getId());
35+
}
36+
37+
public Environment getEnvironment() {
38+
return environment;
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return "ClientboundHelloPacket{" +
44+
"environment=" + environment +
45+
'}';
46+
}
47+
48+
}

0 commit comments

Comments
 (0)