litenetlib-0952 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
litenetlib/__init__.py ADDED
@@ -0,0 +1,66 @@
1
+ """
2
+ LiteNetLib Python - v0.9.5.2 Implementation
3
+ LiteNetLib Python v0.9.5.2 实现
4
+
5
+ Complete Python implementation of LiteNetLib v0.9.5.2 with 100% binary compatibility.
6
+ 完整的 LiteNetLib v0.9.5.2 Python 实现,100% 二进制兼容。
7
+
8
+ Key features / 主要功能:
9
+ - Compatible with C# LiteNetLib v0.9.5.2 / 与 C# LiteNetLib v0.9.5.2 兼容
10
+ - Async I/O with asyncio / 使用 asyncio 的异步 I/O
11
+ - UDP networking / UDP 网络
12
+ - Reliable and unreliable messaging / 可靠和不可靠消息传递
13
+ - Connection management / 连接管理
14
+
15
+ Version differences / 版本差异:
16
+ - PROTOCOL_ID = 11 (not 13) / 协议 ID = 11(不是 13)
17
+ - ACK = 2 (not 3) / ACK = 2(不是 3)
18
+ - EMPTY = 17 (not 18) / EMPTY = 17(不是 18)
19
+ - No ReliableMerged packet type / 无 ReliableMerged 数据包类型
20
+ """
21
+
22
+ from .core.manager import LiteNetManager
23
+ from .core.peer import NetPeer, ConnectionState
24
+ from .core.events import EventBasedNetListener, INetEventListener
25
+ from .core.constants import (
26
+ PacketProperty,
27
+ DeliveryMethod,
28
+ DisconnectReason,
29
+ NetConstants
30
+ )
31
+ from .core.packet import NetPacket, NetPacketPool
32
+ from .core.connection_request import ConnectionRequest
33
+
34
+ from .utils.data_reader import NetDataReader
35
+ from .utils.data_writer import NetDataWriter
36
+ from .utils.fast_bit_converter import FastBitConverter
37
+ from .utils.net_utils import NetUtils
38
+
39
+ __version__ = "1.0.0"
40
+ __protocol_version__ = "0.9.5.2"
41
+
42
+ __all__ = [
43
+ # Core / 核心
44
+ "LiteNetManager",
45
+ "NetPeer",
46
+ "ConnectionState",
47
+ "EventBasedNetListener",
48
+ "INetEventListener",
49
+
50
+ # Constants / 常量
51
+ "PacketProperty",
52
+ "DeliveryMethod",
53
+ "DisconnectReason",
54
+ "NetConstants",
55
+
56
+ # Packets / 数据包
57
+ "NetPacket",
58
+ "NetPacketPool",
59
+ "ConnectionRequest",
60
+
61
+ # Utils / 工具
62
+ "NetDataReader",
63
+ "NetDataWriter",
64
+ "FastBitConverter",
65
+ "NetUtils",
66
+ ]
@@ -0,0 +1,16 @@
1
+ """
2
+ Channel implementations for LiteNetLib v0.9.5.2 / LiteNetLib v0.9.5.2 通道实现
3
+
4
+ Channels manage different delivery methods for packets.
5
+ 通道管理数据包的不同传输方法。
6
+ """
7
+
8
+ from .base_channel import BaseChannel
9
+ from .reliable_channel import ReliableChannel
10
+ from .sequenced_channel import SequencedChannel
11
+
12
+ __all__ = [
13
+ "BaseChannel",
14
+ "ReliableChannel",
15
+ "SequencedChannel",
16
+ ]
@@ -0,0 +1,115 @@
1
+ """
2
+ Base channel class for packet delivery (asyncio-compatible).
3
+
4
+ Channels are responsible for managing packet delivery with specific
5
+ reliability and ordering guarantees.
6
+
7
+ This version uses asyncio while maintaining C# protocol logic.
8
+ """
9
+
10
+ import asyncio
11
+ from abc import ABC, abstractmethod
12
+ from typing import Optional
13
+ from litenetlib.core.packet import NetPacket
14
+ from litenetlib.core.constants import NetConstants
15
+
16
+
17
+ class BaseChannel(ABC):
18
+ """
19
+ Base class for all delivery channels.
20
+
21
+ Channels manage outgoing packet queues and implement different
22
+ delivery strategies (reliable, sequenced, etc.).
23
+
24
+ Uses asyncio.Queue for Python async compatibility while
25
+ maintaining C# protocol logic and algorithms.
26
+ """
27
+
28
+ __slots__ = ('_peer', '_outgoing_queue', '_is_added_to_peer_channel_send_queue')
29
+
30
+ def __init__(self, peer):
31
+ """
32
+ Initialize base channel.
33
+
34
+ Args:
35
+ peer: Associated peer instance
36
+ """
37
+ self._peer = peer
38
+
39
+ # Async queue with capacity DefaultWindowSize (matching C#)
40
+ # C#: Queue<NetPacket> OutgoingQueue = new Queue<NetPacket>(NetConstants.DefaultWindowSize)
41
+ self._outgoing_queue = asyncio.Queue(maxsize=NetConstants.DEFAULT_WINDOW_SIZE)
42
+
43
+ # Atomic flag (C#: Interlocked.CompareExchange)
44
+ self._is_added_to_peer_channel_send_queue = 0
45
+
46
+ @property
47
+ def packets_in_queue(self) -> int:
48
+ """
49
+ Get number of packets in outgoing queue.
50
+
51
+ C# Equivalent: public int PacketsInQueue => OutgoingQueue.Count;
52
+ """
53
+ return self._outgoing_queue.qsize()
54
+
55
+ def add_to_queue(self, packet: NetPacket) -> None:
56
+ """
57
+ Add a packet to the outgoing queue.
58
+
59
+ Args:
60
+ packet: Packet to send
61
+
62
+ C# Equivalent: public void AddToQueue(NetPacket packet)
63
+ """
64
+ # C#: lock (OutgoingQueue) { OutgoingQueue.Enqueue(packet); }
65
+ self._outgoing_queue.put_nowait(packet)
66
+
67
+ # C#: AddToPeerChannelSendQueue()
68
+ self._add_to_peer_channel_send_queue()
69
+
70
+ def _add_to_peer_channel_send_queue(self) -> None:
71
+ """
72
+ Add this channel to peer's channel send queue.
73
+
74
+ C# Equivalent: protected void AddToPeerChannelSendQueue()
75
+ """
76
+ # C#: if (Interlocked.CompareExchange(ref _isAddedToPeerChannelSendQueue, 1, 0) == 0)
77
+ if self._is_added_to_peer_channel_send_queue == 0:
78
+ self._is_added_to_peer_channel_send_queue = 1
79
+ # C#: Peer.AddToReliableChannelSendQueue(this)
80
+ if hasattr(self._peer, 'add_channel_to_send_queue'):
81
+ self._peer.add_channel_to_send_queue(self)
82
+
83
+ @abstractmethod
84
+ def send_next_packets(self) -> bool:
85
+ """
86
+ Send next packets from queue.
87
+
88
+ Returns:
89
+ True if more packets to send, False otherwise
90
+
91
+ C# Equivalent: public abstract bool SendNextPackets();
92
+ """
93
+ pass
94
+
95
+ @abstractmethod
96
+ def process_packet(self, packet: NetPacket) -> bool:
97
+ """
98
+ Process incoming packet.
99
+
100
+ Args:
101
+ packet: Received packet
102
+
103
+ Returns:
104
+ True if packet was processed successfully
105
+
106
+ C# Equivalent: public abstract bool ProcessPacket(NetPacket packet);
107
+ """
108
+ pass
109
+
110
+ def mark_sent(self) -> None:
111
+ """Mark channel as sent (removed from peer send queue)."""
112
+ self._is_added_to_peer_channel_send_queue = 0
113
+
114
+ def __repr__(self) -> str:
115
+ return f"BaseChannel(queued={self.packets_in_queue})"
@@ -0,0 +1,451 @@
1
+ """
2
+ Reliable channel implementation with ACK and retransmission (asyncio-compatible).
3
+
4
+ Implements reliable ordered/unordered delivery with sequence numbers,
5
+ ACK packets, and automatic retransmission.
6
+
7
+ This version uses asyncio for Python compatibility while maintaining
8
+ exact C# protocol logic for interoperability.
9
+
10
+ Ported from: LiteNetLib/ReliableChannel.cs
11
+ """
12
+
13
+ import asyncio
14
+ import time
15
+ from typing import Optional
16
+ from litenetlib.channels.base_channel import BaseChannel
17
+ from litenetlib.core.packet import NetPacket
18
+ from litenetlib.core.constants import PacketProperty, DeliveryMethod, NetConstants
19
+ from litenetlib.utils.net_utils import NetUtils
20
+
21
+ # Constants matching C#
22
+ BITS_IN_BYTE = 8
23
+
24
+
25
+ class PendingPacket:
26
+ """
27
+ A packet awaiting acknowledgment.
28
+
29
+ Matches C# PendingPacket struct protocol logic.
30
+ """
31
+
32
+ __slots__ = ('_packet', '_timestamp', '_is_sent')
33
+
34
+ def __init__(self):
35
+ """Create empty pending packet."""
36
+ self._packet: Optional[NetPacket] = None
37
+ self._timestamp: float = 0.0
38
+ self._is_sent: bool = False
39
+
40
+ def __repr__(self) -> str:
41
+ """String representation."""
42
+ if self._packet is None:
43
+ return "Empty"
44
+ return str(self._packet.sequence)
45
+
46
+ def init(self, packet: NetPacket) -> None:
47
+ """Initialize with a packet."""
48
+ self._packet = packet
49
+ self._is_sent = False
50
+
51
+ def try_send(self, current_time: float, peer) -> bool:
52
+ """
53
+ Try to send packet if resend delay has passed.
54
+
55
+ Args:
56
+ current_time: Current time in seconds
57
+ peer: Peer instance for sending
58
+
59
+ Returns:
60
+ True if packet exists (sent or waiting), False if empty
61
+
62
+ C# Logic: check resendDelay, update timestamp, send packet
63
+ """
64
+ if self._packet is None:
65
+ return False
66
+
67
+ if self._is_sent:
68
+ # C#: double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond
69
+ # We use milliseconds directly
70
+ resend_delay = peer.resend_delay / 1000.0 # Convert to seconds
71
+ packet_hold_time = current_time - self._timestamp
72
+
73
+ if packet_hold_time < resend_delay:
74
+ return True # Still waiting for resend delay
75
+
76
+ # C#: _timeStamp = currentTime; _isSent = true; peer.SendUserData(_packet)
77
+ self._timestamp = current_time
78
+ self._is_sent = True
79
+
80
+ if hasattr(peer, 'send_user_data'):
81
+ peer.send_user_data(self._packet)
82
+
83
+ return True
84
+
85
+ def clear(self, peer) -> bool:
86
+ """
87
+ Clear packet and return to pool.
88
+
89
+ Args:
90
+ peer: Peer instance for recycling
91
+
92
+ Returns:
93
+ True if packet was cleared, False if already empty
94
+ """
95
+ if self._packet is not None:
96
+ # C#: peer.RecycleAndDeliver(_packet)
97
+ if hasattr(peer, 'recycle_and_deliver'):
98
+ peer.recycle_and_deliver(self._packet)
99
+ self._packet = None
100
+ return True
101
+ return False
102
+
103
+
104
+ class ReliableChannel(BaseChannel):
105
+ """
106
+ Reliable channel with ACK/retransmission (asyncio-compatible).
107
+
108
+ Supports both ordered and unordered reliable delivery.
109
+ Implements sliding window protocol with selective ACK.
110
+
111
+ Uses asyncio for Python compatibility while maintaining
112
+ exact C# protocol logic for C# interoperability.
113
+
114
+ C# Reference: internal sealed class ReliableChannel : BaseChannel
115
+ """
116
+
117
+ __slots__ = (
118
+ '_outgoing_acks',
119
+ '_pending_packets',
120
+ '_received_packets',
121
+ '_early_received',
122
+ '_local_sequence',
123
+ '_remote_sequence',
124
+ '_local_window_start',
125
+ '_remote_window_start',
126
+ '_must_send_acks',
127
+ '_delivery_method',
128
+ '_ordered',
129
+ '_window_size',
130
+ '_id',
131
+ )
132
+
133
+ def __init__(self, peer, ordered: bool, channel_id: int):
134
+ """
135
+ Initialize reliable channel.
136
+
137
+ Args:
138
+ peer: Associated peer
139
+ ordered: True for ordered delivery, False for unordered
140
+ channel_id: Channel ID (0-3)
141
+
142
+ C# Equivalent: public ReliableChannel(LiteNetPeer peer, bool ordered, byte id)
143
+ """
144
+ super().__init__(peer)
145
+
146
+ self._id = channel_id
147
+ self._window_size = NetConstants.DEFAULT_WINDOW_SIZE
148
+ self._ordered = ordered
149
+
150
+ # Create pending packets array
151
+ # C#: _pendingPackets = new PendingPacket[_windowSize];
152
+ self._pending_packets = [PendingPacket() for _ in range(self._window_size)]
153
+
154
+ # Delivery method
155
+ # C#: _deliveryMethod = DeliveryMethod.ReliableOrdered : DeliveryMethod.ReliableUnordered
156
+ if ordered:
157
+ self._delivery_method = DeliveryMethod.RELIABLE_ORDERED
158
+ # C#: _receivedPackets = new NetPacket[_windowSize]
159
+ self._received_packets = [None] * self._window_size
160
+ self._early_received = None
161
+ else:
162
+ self._delivery_method = DeliveryMethod.RELIABLE_UNORDERED
163
+ self._received_packets = None
164
+ # C#: _earlyReceived = new bool[_windowSize]
165
+ self._early_received = [False] * self._window_size
166
+
167
+ # Initialize sequence numbers
168
+ # C#: _localWindowStart = 0; _localSeqence = 0; _remoteSequence = 0; _remoteWindowStart = 0;
169
+ self._local_window_start = 0
170
+ self._local_sequence = 0
171
+ self._remote_sequence = 0
172
+ self._remote_window_start = 0
173
+
174
+ # Create ACK packet
175
+ # C#: _outgoingAcks = new NetPacket(PacketProperty.Ack, (_windowSize - 1) / BitsInByte + 2)
176
+ ack_data_size = (self._window_size - 1) // BITS_IN_BYTE + 2
177
+ self._outgoing_acks = NetPacket(PacketProperty.ACK, ack_data_size)
178
+ self._outgoing_acks.channel_id = channel_id
179
+
180
+ self._must_send_acks = False
181
+
182
+ def send_next_packets(self) -> bool:
183
+ """
184
+ Send next packets from queue and handle retransmissions.
185
+
186
+ Returns:
187
+ True if more packets to send, False otherwise
188
+
189
+ C# Equivalent: public override bool SendNextPackets()
190
+ """
191
+ # Send ACKs if needed
192
+ # C#: if (_mustSendAcks) { ... }
193
+ if self._must_send_acks:
194
+ self._must_send_acks = False
195
+ # C#: lock(_outgoingAcks) Peer.SendUserData(_outgoingAcks)
196
+ if hasattr(self._peer, 'send_user_data'):
197
+ # Send ACK packet (copy it since it's reused)
198
+ ack_copy = NetPacket.from_bytes(self._outgoing_acks.get_bytes())
199
+ self._peer.send_user_data(ack_copy)
200
+
201
+ # Get current time in seconds
202
+ current_time = time.time()
203
+ has_pending_packets = False
204
+
205
+ # Get packets from queue and add to pending
206
+ # C#: lock (OutgoingQueue) { while (OutgoingQueue.Count > 0) ... }
207
+ while not self._outgoing_queue.empty():
208
+ # C#: int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart)
209
+ relate = NetUtils.relative_sequence_number(self._local_sequence, self._local_window_start)
210
+ if relate >= self._window_size:
211
+ break # Window full
212
+
213
+ # C#: var netPacket = OutgoingQueue.Dequeue()
214
+ try:
215
+ net_packet = self._outgoing_queue.get_nowait()
216
+ except asyncio.QueueEmpty:
217
+ break
218
+
219
+ # Set sequence and channel
220
+ # C#: netPacket.Sequence = (ushort) _localSeqence; netPacket.ChannelId = _id;
221
+ net_packet.sequence = self._local_sequence & 0xFFFF
222
+ net_packet.channel_id = self._id
223
+
224
+ # Add to pending
225
+ # C#: _pendingPackets[_localSeqence % _windowSize].Init(netPacket)
226
+ self._pending_packets[self._local_sequence % self._window_size].init(net_packet)
227
+
228
+ # Advance local sequence
229
+ # C#: _localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence
230
+ self._local_sequence = (self._local_sequence + 1) % NetConstants.MAX_SEQUENCE
231
+
232
+ # Send pending packets
233
+ # C#: for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = ...)
234
+ pending_seq = self._local_window_start
235
+ while pending_seq != self._local_sequence:
236
+ # C#: _pendingPackets[pendingSeq % _windowSize].TrySend(currentTime, Peer)
237
+ if self._pending_packets[pending_seq % self._window_size].try_send(current_time, self._peer):
238
+ has_pending_packets = True
239
+
240
+ pending_seq = (pending_seq + 1) % NetConstants.MAX_SEQUENCE
241
+
242
+ # C#: return hasPendingPackets || _mustSendAcks || OutgoingQueue.Count > 0
243
+ return has_pending_packets or self._must_send_acks or not self._outgoing_queue.empty()
244
+
245
+ def process_packet(self, packet: NetPacket) -> bool:
246
+ """
247
+ Process incoming packet.
248
+
249
+ Args:
250
+ packet: Received packet
251
+
252
+ Returns:
253
+ True if packet was processed successfully
254
+
255
+ C# Equivalent: public override bool ProcessPacket(NetPacket packet)
256
+ """
257
+ # Check if ACK packet
258
+ # C#: if (packet.Property == PacketProperty.Ack) { ProcessAck(packet); return false; }
259
+ if packet.packet_property == PacketProperty.ACK:
260
+ self._process_ack(packet)
261
+ return False
262
+
263
+ seq = packet.sequence
264
+
265
+ # Validate sequence
266
+ # C#: if (seq >= NetConstants.MaxSequence) { ... }
267
+ if seq >= NetConstants.MAX_SEQUENCE:
268
+ return False
269
+
270
+ # C#: int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart)
271
+ # int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence)
272
+ relate = NetUtils.relative_sequence_number(seq, self._remote_window_start)
273
+ relate_seq = NetUtils.relative_sequence_number(seq, self._remote_sequence)
274
+
275
+ # C#: if (relateSeq > _windowSize) { ... }
276
+ if relate_seq > self._window_size:
277
+ return False
278
+
279
+ # Drop old packets
280
+ # C#: if (relate < 0) { ... }
281
+ if relate < 0:
282
+ return False
283
+
284
+ # C#: if (relate >= _windowSize * 2) { ... }
285
+ if relate >= self._window_size * 2:
286
+ return False
287
+
288
+ # Process ACK bitmap and window
289
+ # C#: if (relate >= _windowSize) { ... }
290
+ if relate >= self._window_size:
291
+ # Calculate new window start
292
+ # C#: int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence
293
+ new_window_start = (self._remote_window_start + relate - self._window_size + 1) % NetConstants.MAX_SEQUENCE
294
+ self._outgoing_acks.sequence = new_window_start & 0xFFFF
295
+
296
+ # Clean old ACK data
297
+ # C#: while (_remoteWindowStart != newWindowStart) { ... }
298
+ while self._remote_window_start != new_window_start:
299
+ ack_idx = self._remote_window_start % self._window_size
300
+ ack_byte = NetConstants.CHANNELED_HEADER_SIZE + ack_idx // BITS_IN_BYTE
301
+ ack_bit = ack_idx % BITS_IN_BYTE
302
+ # C#: _outgoingAcks.RawData[ackByte] &= (byte) ~(1 << ackBit)
303
+ self._outgoing_acks._data[ack_byte] &= ~(1 << ack_bit)
304
+ self._remote_window_start = (self._remote_window_start + 1) % NetConstants.MAX_SEQUENCE
305
+
306
+ # Trigger ACKs send
307
+ # C#: _mustSendAcks = true
308
+ self._must_send_acks = True
309
+
310
+ # Calculate ACK position
311
+ # C#: ackIdx = seq % _windowSize
312
+ # ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte
313
+ # ackBit = ackIdx % BitsInByte
314
+ ack_idx = seq % self._window_size
315
+ ack_byte = NetConstants.CHANNELED_HEADER_SIZE + ack_idx // BITS_IN_BYTE
316
+ ack_bit = ack_idx % BITS_IN_BYTE
317
+
318
+ # Check for duplicate
319
+ # C#: if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) { ... }
320
+ if (self._outgoing_acks._data[ack_byte] & (1 << ack_bit)) != 0:
321
+ # Duplicate packet
322
+ # C#: AddToPeerChannelSendQueue(); return false
323
+ self._add_to_peer_channel_send_queue()
324
+ return False
325
+
326
+ # Save ACK
327
+ # C#: _outgoingAcks.RawData[ackByte] |= (byte) (1 << ackBit)
328
+ self._outgoing_acks._data[ack_byte] |= (1 << ack_bit)
329
+
330
+ # Trigger send
331
+ # C#: AddToPeerChannelSendQueue()
332
+ self._add_to_peer_channel_send_queue()
333
+
334
+ # Detailed check - expected packet?
335
+ # C#: if (seq == _remoteSequence) { ... }
336
+ if seq == self._remote_sequence:
337
+ # Expected packet - deliver immediately
338
+ # C#: Peer.AddReliablePacket(_deliveryMethod, packet)
339
+ if hasattr(self._peer, 'add_reliable_packet'):
340
+ self._peer.add_reliable_packet(self._delivery_method, packet)
341
+
342
+ # Advance sequence
343
+ # C#: _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence
344
+ self._remote_sequence = (self._remote_sequence + 1) % NetConstants.MAX_SEQUENCE
345
+
346
+ # Process held packets (for ordered mode)
347
+ if self._ordered:
348
+ # C#: while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) { ... }
349
+ while True:
350
+ idx = self._remote_sequence % self._window_size
351
+ p = self._received_packets[idx]
352
+ if p is None:
353
+ break
354
+ # C#: _receivedPackets[_remoteSequence % _windowSize] = null
355
+ self._received_packets[idx] = None
356
+ # C#: Peer.AddReliablePacket(_deliveryMethod, p)
357
+ if hasattr(self._peer, 'add_reliable_packet'):
358
+ self._peer.add_reliable_packet(self._delivery_method, p)
359
+ # C#: _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence
360
+ self._remote_sequence = (self._remote_sequence + 1) % NetConstants.MAX_SEQUENCE
361
+ else:
362
+ # Unordered - process early received flags
363
+ # C#: while (_earlyReceived[_remoteSequence % _windowSize]) { ... }
364
+ while self._early_received[self._remote_sequence % self._window_size]:
365
+ self._early_received[self._remote_sequence % self._window_size] = False
366
+ self._remote_sequence = (self._remote_sequence + 1) % NetConstants.MAX_SEQUENCE
367
+
368
+ return True
369
+
370
+ # Hold packet for later delivery
371
+ # C#: //holden packet
372
+ if self._ordered:
373
+ # C#: _receivedPackets[ackIdx] = packet
374
+ self._received_packets[ack_idx] = packet
375
+ else:
376
+ # C#: _earlyReceived[ackIdx] = true; Peer.AddReliablePacket(_deliveryMethod, packet)
377
+ self._early_received[ack_idx] = True
378
+ if hasattr(self._peer, 'add_reliable_packet'):
379
+ self._peer.add_reliable_packet(self._delivery_method, packet)
380
+
381
+ return True
382
+
383
+ def _process_ack(self, packet: NetPacket) -> None:
384
+ """
385
+ Process ACK packet.
386
+
387
+ Args:
388
+ packet: ACK packet to process
389
+
390
+ C# Equivalent: private void ProcessAck(NetPacket packet)
391
+ """
392
+ # Validate ACK packet size
393
+ # C#: if (packet.Size != _outgoingAcks.Size) { ... }
394
+ if packet.size != self._outgoing_acks.size:
395
+ return
396
+
397
+ ack_window_start = packet.sequence
398
+ window_rel = NetUtils.relative_sequence_number(self._local_window_start, ack_window_start)
399
+
400
+ # Validate window start
401
+ # C#: if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) { ... }
402
+ if ack_window_start >= NetConstants.MAX_SEQUENCE or window_rel < 0:
403
+ return
404
+
405
+ # Check relevance
406
+ # C#: if (windowRel >= _windowSize) { ... }
407
+ if window_rel >= self._window_size:
408
+ return
409
+
410
+ acks_data = packet._data
411
+
412
+ # Process acknowledged packets
413
+ # C#: for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = ...)
414
+ pending_seq = self._local_window_start
415
+ while pending_seq != self._local_sequence:
416
+ rel = NetUtils.relative_sequence_number(pending_seq, ack_window_start)
417
+
418
+ # C#: if (rel >= _windowSize) { ... }
419
+ if rel >= self._window_size:
420
+ break
421
+
422
+ # Calculate position in ACK bitmap
423
+ # C#: int pendingIdx = pendingSeq % _windowSize
424
+ # int currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte
425
+ # int currentBit = pendingIdx % BitsInByte
426
+ pending_idx = pending_seq % self._window_size
427
+ current_byte = NetConstants.CHANNELED_HEADER_SIZE + pending_idx // BITS_IN_BYTE
428
+ current_bit = pending_idx % BITS_IN_BYTE
429
+
430
+ # Check if packet was acknowledged
431
+ # C#: if ((acksData[currentByte] & (1 << currentBit)) == 0) { ... }
432
+ if (acks_data[current_byte] & (1 << current_bit)) == 0:
433
+ # Packet not acknowledged yet - skip
434
+ pending_seq = (pending_seq + 1) % NetConstants.MAX_SEQUENCE
435
+ continue
436
+
437
+ # Move window if this is the start
438
+ # C#: if (pendingSeq == _localWindowStart) { _localWindowStart = ... }
439
+ if pending_seq == self._local_window_start:
440
+ self._local_window_start = (self._local_window_start + 1) % NetConstants.MAX_SEQUENCE
441
+
442
+ # Clear packet
443
+ # C#: if (_pendingPackets[pendingIdx].Clear(Peer)) { ... }
444
+ self._pending_packets[pending_idx].clear(self._peer)
445
+
446
+ pending_seq = (pending_seq + 1) % NetConstants.MAX_SEQUENCE
447
+
448
+ def __repr__(self) -> str:
449
+ ordered_str = "ordered" if self._ordered else "unordered"
450
+ return (f"ReliableChannel(id={self._id}, {ordered_str}, "
451
+ f"local_seq={self._local_sequence}, remote_seq={self._remote_sequence})")