Gameplay packets accepted before joining
cuwo/server.py
Why it changed
The packet dispatcher ran every handler regardless of whether the client had finished the handshake. A socket that had sent nothing but a version number could already drop items, interact with chunks, fire hit packets and drive world state.
The worst of these was interact: it queued terrain generation, so a client that never joined could keep the generation thread busy indefinitely.
Only two packets are meaningful before joining — the version handshake, and the entity update that carries the player name and triggers the join. Everything else is now dropped, with one log line per connection so the console does not fill up.
Code changes
@@ -42,6 +44,13 @@
join_packet = packets.JoinPacket()
seed_packet = packets.SeedData()
chat_packet = packets.ServerChatMessage()
+
+
+# Packets a client may send before it has joined: the version handshake, and
+# the entity update that carries the player name (which is what triggers
+# on_join). Everything else is rejected until the join completes.
+PREJOIN_PACKETS = frozenset((packets.ClientVersion.packet_id,
+ packets.EntityUpdate.packet_id))
entity_packet = packets.EntityUpdate()
@@ -128,6 +137,7 @@
has_joined = False
+ prejoin_warned = False
entity_id = None
@@ -253,6 +263,18 @@
if packet is None:
self.on_invalid_packet('data')
return
+ # Gameplay packets are only meaningful once the client has completed
+ # the handshake and joined. Accepting them earlier let an unjoined
+ # socket drive world state -- most seriously, interact packets could
+ # queue terrain generation for arbitrary chunks. Only the version
+ # handshake and the entity update that carries the name (which is
+ # what triggers on_join) are valid before joining.
+ if not self.has_joined and packet.packet_id not in PREJOIN_PACKETS:
+ if not self.prejoin_warned:
+ self.prejoin_warned = True
+ print('Ignoring pre-join packet %s from %s'
+ % (packet.packet_id, self.address[0]))
+ return
handler = self.packet_handlers.get(packet.packet_id, None)