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 +66 -0
- litenetlib/channels/__init__.py +16 -0
- litenetlib/channels/base_channel.py +115 -0
- litenetlib/channels/reliable_channel.py +451 -0
- litenetlib/channels/sequenced_channel.py +239 -0
- litenetlib/core/__init__.py +53 -0
- litenetlib/core/connection_request.py +240 -0
- litenetlib/core/constants.py +186 -0
- litenetlib/core/events.py +336 -0
- litenetlib/core/internal_packets.py +358 -0
- litenetlib/core/manager.py +355 -0
- litenetlib/core/packet.py +457 -0
- litenetlib/core/peer.py +334 -0
- litenetlib/utils/__init__.py +21 -0
- litenetlib/utils/data_reader.py +447 -0
- litenetlib/utils/data_writer.py +402 -0
- litenetlib/utils/fast_bit_converter.py +199 -0
- litenetlib/utils/net_utils.py +165 -0
- litenetlib_0952-1.0.0.dist-info/METADATA +448 -0
- litenetlib_0952-1.0.0.dist-info/RECORD +23 -0
- litenetlib_0952-1.0.0.dist-info/WHEEL +5 -0
- litenetlib_0952-1.0.0.dist-info/licenses/LICENSE +21 -0
- litenetlib_0952-1.0.0.dist-info/top_level.txt +1 -0
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})")
|