|
1 | 1 | package com.lambda.module.modules.network |
2 | 2 |
|
| 3 | +import com.lambda.context.SafeContext |
3 | 4 | import com.lambda.event.events.PacketEvent |
| 5 | +import com.lambda.event.events.RenderEvent |
4 | 6 | import com.lambda.event.listener.SafeListener.Companion.listener |
5 | 7 | import com.lambda.module.Module |
6 | 8 | import com.lambda.module.tag.ModuleTag |
7 | 9 | import com.lambda.threading.runConcurrent |
8 | 10 | import com.lambda.threading.runGameScheduled |
| 11 | +import com.lambda.util.PacketUtils.handlePacketSilently |
| 12 | +import com.lambda.util.PacketUtils.sendPacketSilently |
9 | 13 | import kotlinx.coroutines.delay |
10 | | -import net.minecraft.network.ClientConnection |
| 14 | +import net.minecraft.network.listener.ClientPacketListener |
| 15 | +import net.minecraft.network.listener.ServerPacketListener |
11 | 16 | import net.minecraft.network.packet.Packet |
12 | 17 | import net.minecraft.network.packet.c2s.common.KeepAliveC2SPacket |
| 18 | +import java.util.concurrent.ConcurrentLinkedDeque |
13 | 19 |
|
14 | 20 | object PacketDelay : Module( |
15 | 21 | name = "PacketDelay", |
16 | 22 | description = "Delays packets client-side & server-side.", |
17 | 23 | defaultTags = setOf(ModuleTag.NETWORK), |
18 | 24 | ) { |
| 25 | + private val mode by setting("Mode", Mode.STATIC) |
19 | 26 | private val networkScope by setting("Network Scope", Direction.BOTH) |
20 | 27 | private val packetScope by setting("Packet Scope", PacketType.ANY) |
21 | 28 | private val inboundDelay by setting("Inbound Delay", 250L, 0L..5000L, 10L, unit = "ms") { networkScope != Direction.OUTBOUND } |
22 | 29 | private val outboundDelay by setting("Outbound Delay", 250L, 0L..5000L, 10L, unit = "ms") { networkScope != Direction.INBOUND } |
23 | 30 |
|
24 | | - enum class Direction { |
25 | | - BOTH, |
26 | | - INBOUND, |
27 | | - OUTBOUND |
28 | | - } |
29 | | - |
30 | | - enum class PacketType(val filter: (Packet<*>) -> Boolean) { |
31 | | - ANY({ true }), |
32 | | - KEEP_ALIVE({ it is KeepAliveC2SPacket }) |
33 | | - } |
| 31 | + private var outboundPool = ConcurrentLinkedDeque<Packet<out ServerPacketListener>>() |
| 32 | + private var inboundPool = ConcurrentLinkedDeque<Packet<out ClientPacketListener>>() |
| 33 | + private var outboundLastUpdate = 0L |
| 34 | + private var inboundLastUpdate = 0L |
34 | 35 |
|
35 | 36 | init { |
36 | | - listener<PacketEvent.Receive.Pre>(Int.MIN_VALUE) { event -> |
37 | | - if (!connection.connection.isOpen) return@listener |
| 37 | + listener<RenderEvent.World> { |
| 38 | + if (mode != Mode.STATIC) return@listener |
| 39 | + |
| 40 | + flushPools(System.currentTimeMillis()) |
| 41 | + } |
| 42 | + |
| 43 | + listener<PacketEvent.Send.Pre>(Int.MIN_VALUE) { event -> |
38 | 44 | if (!packetScope.filter(event.packet)) return@listener |
39 | | - event.cancel() |
40 | 45 |
|
41 | | - runConcurrent { |
42 | | - delay(inboundDelay) |
43 | | - runGameScheduled { |
44 | | - if (connection.connection.packetListener?.accepts(event.packet) == false) return@runGameScheduled |
| 46 | + when (mode) { |
| 47 | + Mode.STATIC -> { |
| 48 | + outboundPool.add(event.packet) |
| 49 | + event.cancel() |
| 50 | + } |
45 | 51 |
|
46 | | - ClientConnection.handlePacket(event.packet, connection.connection.packetListener) |
47 | | - connection.connection.packetsReceivedCounter++ |
| 52 | + Mode.PULSE -> { |
| 53 | + runConcurrent { |
| 54 | + delay(outboundDelay) |
| 55 | + runGameScheduled { |
| 56 | + connection.sendPacketSilently(event.packet) |
| 57 | + } |
| 58 | + } |
| 59 | + event.cancel() |
48 | 60 | } |
49 | 61 | } |
50 | 62 | } |
51 | 63 |
|
52 | | - listener<PacketEvent.Send.Pre>(Int.MIN_VALUE) { event -> |
53 | | - if (!connection.connection.isOpen) return@listener |
| 64 | + listener<PacketEvent.Receive.Pre>(Int.MIN_VALUE) { event -> |
54 | 65 | if (!packetScope.filter(event.packet)) return@listener |
| 66 | + |
| 67 | + when (mode) { |
| 68 | + Mode.STATIC -> { |
| 69 | + inboundPool.add(event.packet) |
| 70 | + event.cancel() |
| 71 | + } |
| 72 | + |
| 73 | + Mode.PULSE -> { |
| 74 | + runConcurrent { |
| 75 | + delay(inboundDelay) |
| 76 | + runGameScheduled { |
| 77 | + connection.handlePacketSilently(event.packet) |
| 78 | + } |
| 79 | + } |
| 80 | + event.cancel() |
| 81 | + } |
| 82 | + } |
| 83 | + |
55 | 84 | event.cancel() |
| 85 | + } |
| 86 | + |
| 87 | + onDisable { |
| 88 | + flushPools(System.currentTimeMillis()) |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + private fun SafeContext.flushPools(time: Long) { |
| 93 | + if (time - outboundLastUpdate >= outboundDelay) { |
| 94 | + while (outboundPool.isNotEmpty()) { |
| 95 | + outboundPool.poll().let { packet -> |
| 96 | + connection.sendPacketSilently(packet) |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + outboundLastUpdate = time |
| 101 | + } |
56 | 102 |
|
57 | | - runConcurrent { |
58 | | - delay(outboundDelay) |
59 | | - runGameScheduled { |
60 | | - connection.connection.send(event.packet, null) |
61 | | - connection.connection.packetsSentCounter++ |
| 103 | + if (time - inboundLastUpdate >= inboundDelay) { |
| 104 | + while (inboundPool.isNotEmpty()) { |
| 105 | + inboundPool.poll().let { packet -> |
| 106 | + connection.handlePacketSilently(packet) |
62 | 107 | } |
63 | 108 | } |
| 109 | + |
| 110 | + inboundLastUpdate = time |
64 | 111 | } |
65 | 112 | } |
| 113 | + |
| 114 | + enum class Mode { STATIC, PULSE, } |
| 115 | + enum class Direction { BOTH, INBOUND, OUTBOUND } |
| 116 | + enum class PacketType(val filter: (Packet<*>) -> Boolean) { |
| 117 | + ANY({ true }), |
| 118 | + KEEP_ALIVE({ it is KeepAliveC2SPacket }) |
| 119 | + } |
66 | 120 | } |
0 commit comments