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.
@@ -0,0 +1,239 @@
1
+ """
2
+ Sequenced channel implementation (asyncio-compatible).
3
+
4
+ Implements sequenced and reliable sequenced delivery where only
5
+ the newest packet matters (older packets are dropped).
6
+
7
+ This version uses asyncio for Python compatibility while maintaining
8
+ exact C# protocol logic for interoperability.
9
+
10
+ Ported from: LiteNetLib/SequencedChannel.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
+
22
+ class SequencedChannel(BaseChannel):
23
+ """
24
+ Sequenced channel with optional reliability (asyncio-compatible).
25
+
26
+ Sequenced: Can drop packets, no duplicates, arrives in order.
27
+ ReliableSequenced: Only the last packet is reliable, cannot be fragmented.
28
+
29
+ Uses asyncio for Python compatibility while maintaining
30
+ exact C# protocol logic for C# interoperability.
31
+
32
+ C# Reference: internal sealed class SequencedChannel : BaseChannel
33
+ """
34
+
35
+ __slots__ = (
36
+ '_local_sequence',
37
+ '_remote_sequence',
38
+ '_reliable',
39
+ '_last_packet',
40
+ '_ack_packet',
41
+ '_must_send_ack',
42
+ '_id',
43
+ '_last_packet_send_time',
44
+ )
45
+
46
+ def __init__(self, peer, reliable: bool, channel_id: int):
47
+ """
48
+ Initialize sequenced channel.
49
+
50
+ Args:
51
+ peer: Associated peer
52
+ reliable: True for ReliableSequenced, False for Sequenced
53
+ channel_id: Channel ID (1 or 3)
54
+
55
+ C# Equivalent: public SequencedChannel(LiteNetPeer peer, bool reliable, byte id)
56
+ """
57
+ super().__init__(peer)
58
+
59
+ self._id = channel_id
60
+ self._reliable = reliable
61
+
62
+ # C#: _ackPacket = new NetPacket(PacketProperty.Ack, 0) {ChannelId = id};
63
+ if self._reliable:
64
+ self._ack_packet = NetPacket(PacketProperty.ACK, 0)
65
+ self._ack_packet.channel_id = channel_id
66
+ else:
67
+ self._ack_packet = None
68
+
69
+ # Initialize sequences
70
+ # C#: _localSequence = 0; _remoteSequence = 0;
71
+ self._local_sequence = 0
72
+ self._remote_sequence = 0
73
+
74
+ # Last packet (for reliable sequenced)
75
+ self._last_packet: Optional[NetPacket] = None
76
+ self._last_packet_send_time: float = 0.0
77
+
78
+ # ACK flag
79
+ self._must_send_ack = False
80
+
81
+ def send_next_packets(self) -> bool:
82
+ """
83
+ Send next packets from queue.
84
+
85
+ Returns:
86
+ True if more packets to send, False otherwise
87
+
88
+ C# Equivalent: public override bool SendNextPackets()
89
+ """
90
+ # C#: if (_reliable && OutgoingQueue.Count == 0)
91
+ if self._reliable and self._outgoing_queue.empty():
92
+ # Resend last packet if needed (reliable sequenced)
93
+ # C#: long packetHoldTime = currentTime - _lastPacketSendTime;
94
+ # if (packetHoldTime >= Peer.ResendDelay * TimeSpan.TicksPerMillisecond)
95
+ current_time = time.time()
96
+ packet_hold_time = current_time - self._last_packet_send_time
97
+ resend_delay = self._peer.resend_delay / 1000.0 # Convert to seconds
98
+
99
+ if packet_hold_time >= resend_delay:
100
+ packet = self._last_packet
101
+ if packet is not None:
102
+ # C#: _lastPacketSendTime = currentTime; Peer.SendUserData(packet);
103
+ self._last_packet_send_time = current_time
104
+ if hasattr(self._peer, 'send_user_data'):
105
+ self._peer.send_user_data(packet)
106
+ else:
107
+ # Send new packets
108
+ # C#: while (OutgoingQueue.Count > 0)
109
+ while not self._outgoing_queue.empty():
110
+ try:
111
+ # C#: NetPacket packet = OutgoingQueue.Dequeue();
112
+ packet = self._outgoing_queue.get_nowait()
113
+ except asyncio.QueueEmpty:
114
+ break
115
+
116
+ # Advance sequence and set packet properties
117
+ # C#: _localSequence = (_localSequence + 1) % NetConstants.MaxSequence;
118
+ # packet.Sequence = (ushort)_localSequence;
119
+ # packet.ChannelId = _id;
120
+ self._local_sequence = (self._local_sequence + 1) % NetConstants.MAX_SEQUENCE
121
+ packet.sequence = self._local_sequence & 0xFFFF
122
+ packet.channel_id = self._id
123
+
124
+ # Send packet
125
+ # C#: Peer.SendUserData(packet);
126
+ if hasattr(self._peer, 'send_user_data'):
127
+ self._peer.send_user_data(packet)
128
+
129
+ # Handle reliable sequenced (keep last packet)
130
+ # C#: if (_reliable && OutgoingQueue.Count == 0)
131
+ # {
132
+ # _lastPacketSendTime = DateTime.UtcNow.Ticks;
133
+ # _lastPacket = packet;
134
+ # }
135
+ # else
136
+ # {
137
+ # Peer.NetManager.PoolRecycle(packet);
138
+ # }
139
+ if self._reliable and self._outgoing_queue.empty():
140
+ self._last_packet_send_time = time.time()
141
+ self._last_packet = packet
142
+ else:
143
+ # Packet sent, will be recycled by peer
144
+ pass
145
+
146
+ # Send ACK if needed
147
+ # C#: if (_reliable && _mustSendAck)
148
+ # {
149
+ # _mustSendAck = false;
150
+ # _ackPacket.Sequence = _remoteSequence;
151
+ # Peer.SendUserData(_ackPacket);
152
+ # }
153
+ if self._reliable and self._must_send_ack:
154
+ self._must_send_ack = False
155
+ self._ack_packet.sequence = self._remote_sequence & 0xFFFF
156
+ if hasattr(self._peer, 'send_user_data'):
157
+ # Send ACK packet (copy it since it's reused)
158
+ ack_copy = NetPacket.from_bytes(self._ack_packet.get_bytes())
159
+ self._peer.send_user_data(ack_copy)
160
+
161
+ # C#: return _lastPacket != null;
162
+ return self._last_packet is not None
163
+
164
+ def process_packet(self, packet: NetPacket) -> bool:
165
+ """
166
+ Process incoming packet.
167
+
168
+ Args:
169
+ packet: Received packet
170
+
171
+ Returns:
172
+ True if packet was processed successfully
173
+
174
+ C# Equivalent: public override bool ProcessPacket(NetPacket packet)
175
+ """
176
+ # C#: if (packet.IsFragmented) return false;
177
+ if packet.is_fragmented:
178
+ return False
179
+
180
+ # Handle ACK packet
181
+ # C#: if (packet.Property == PacketProperty.Ack)
182
+ # {
183
+ # if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Sequence)
184
+ # _lastPacket = null;
185
+ # return false;
186
+ # }
187
+ if packet.packet_property == PacketProperty.ACK:
188
+ if self._reliable and self._last_packet is not None and packet.sequence == self._last_packet.sequence:
189
+ self._last_packet = None
190
+ return False
191
+
192
+ # Calculate relative sequence
193
+ # C#: int relative = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence);
194
+ relative = NetUtils.relative_sequence_number(packet.sequence, self._remote_sequence)
195
+ packet_processed = False
196
+
197
+ # C#: if (packet.Sequence < NetConstants.MaxSequence && relative > 0)
198
+ if packet.sequence < NetConstants.MAX_SEQUENCE and relative > 0:
199
+ # Newer packet received - drop older packets
200
+ # C#: if (Peer.NetManager.EnableStatistics)
201
+ # {
202
+ # Peer.Statistics.AddPacketLoss(relative - 1);
203
+ # Peer.NetManager.Statistics.AddPacketLoss(relative - 1);
204
+ # }
205
+ # _remoteSequence = packet.Sequence;
206
+
207
+ # Note: Statistics not implemented in Python yet
208
+ self._remote_sequence = packet.sequence
209
+
210
+ # Deliver packet to peer
211
+ # C#: Peer.NetManager.CreateReceiveEvent(
212
+ # packet,
213
+ # _reliable ? DeliveryMethod.ReliableSequenced : DeliveryMethod.Sequenced,
214
+ # (byte)(packet.ChannelId / NetConstants.ChannelTypeCount),
215
+ # NetConstants.ChanneledHeaderSize,
216
+ # Peer);
217
+
218
+ if hasattr(self._peer, 'add_reliable_packet'):
219
+ delivery_method = DeliveryMethod.RELIABLE_SEQUENCED if self._reliable else DeliveryMethod.SEQUENCED
220
+ self._peer.add_reliable_packet(delivery_method, packet)
221
+
222
+ packet_processed = True
223
+
224
+ # Send ACK if reliable
225
+ # C#: if (_reliable)
226
+ # {
227
+ # _mustSendAck = true;
228
+ # AddToPeerChannelSendQueue();
229
+ # }
230
+ if self._reliable:
231
+ self._must_send_ack = True
232
+ self._add_to_peer_channel_send_queue()
233
+
234
+ return packet_processed
235
+
236
+ def __repr__(self) -> str:
237
+ reliable_str = "reliable" if self._reliable else "unreliable"
238
+ return (f"SequencedChannel(id={self._id}, {reliable_str}, "
239
+ f"local_seq={self._local_sequence}, remote_seq={self._remote_sequence})")
@@ -0,0 +1,53 @@
1
+ """
2
+ Core modules for LiteNetLib v0.9.5.2 / LiteNetLib v0.9.5.2 核心模块
3
+ """
4
+
5
+ from .constants import (
6
+ PacketProperty,
7
+ DeliveryMethod,
8
+ DisconnectReason,
9
+ NetConstants,
10
+ get_header_size
11
+ )
12
+ from .manager import LiteNetManager
13
+ from .peer import NetPeer, ConnectionState
14
+ from .events import EventBasedNetListener, INetEventListener
15
+ from .packet import NetPacket, NetPacketPool
16
+ from .connection_request import ConnectionRequest
17
+ from .internal_packets import (
18
+ NetConnectRequestPacket,
19
+ NetConnectAcceptPacket,
20
+ serialize_address,
21
+ deserialize_address
22
+ )
23
+
24
+ __all__ = [
25
+ # Constants / 常量
26
+ "PacketProperty",
27
+ "DeliveryMethod",
28
+ "DisconnectReason",
29
+ "NetConstants",
30
+ "get_header_size",
31
+
32
+ # Manager / 管理器
33
+ "LiteNetManager",
34
+
35
+ # Peer / 对等端
36
+ "NetPeer",
37
+ "ConnectionState",
38
+
39
+ # Events / 事件
40
+ "EventBasedNetListener",
41
+ "INetEventListener",
42
+
43
+ # Packets / 数据包
44
+ "NetPacket",
45
+ "NetPacketPool",
46
+ "ConnectionRequest",
47
+
48
+ # Internal packets / 内部数据包
49
+ "NetConnectRequestPacket",
50
+ "NetConnectAcceptPacket",
51
+ "serialize_address",
52
+ "deserialize_address",
53
+ ]
@@ -0,0 +1,240 @@
1
+ """
2
+ Connection request handling for LiteNetLib.
3
+
4
+ This module manages incoming connection requests from remote peers,
5
+ providing methods to accept or reject connections.
6
+
7
+ Ported from: LiteNetLib/ConnectionRequest.cs
8
+ """
9
+
10
+ import threading
11
+ from typing import Optional, Tuple
12
+ from litenetlib.core.internal_packets import NetConnectRequestPacket
13
+ from litenetlib.utils.data_reader import NetDataReader
14
+ from litenetlib.utils.data_writer import NetDataWriter
15
+
16
+
17
+ class ConnectionRequestResult:
18
+ """Result of connection request processing."""
19
+ NONE = 0
20
+ ACCEPT = 1
21
+ REJECT = 2
22
+ REJECT_FORCE = 3
23
+
24
+
25
+ class ConnectionRequest:
26
+ """
27
+ Represents an incoming connection request.
28
+
29
+ Provides methods to accept or reject the connection, with optional
30
+ additional data for rejection reason.
31
+
32
+ Thread-safe: Each request can only be processed once (accept or reject).
33
+
34
+ C# Reference: ConnectionRequest class
35
+ """
36
+
37
+ __slots__ = (
38
+ '_manager',
39
+ '_used',
40
+ '_result',
41
+ '_internal_packet',
42
+ '_remote_address'
43
+ )
44
+
45
+ def __init__(
46
+ self,
47
+ remote_address: Tuple[str, int],
48
+ request_packet: NetConnectRequestPacket,
49
+ manager
50
+ ):
51
+ """
52
+ Create connection request.
53
+
54
+ Args:
55
+ remote_address: Remote endpoint (host, port)
56
+ request_packet: Connection request packet
57
+ manager: Network manager instance (for callback)
58
+ """
59
+ self._manager = manager
60
+ self._used = 0 # Atomic-like flag for thread safety
61
+ self._result = ConnectionRequestResult.NONE
62
+ self._internal_packet = request_packet
63
+ self._remote_address = remote_address
64
+
65
+ @property
66
+ def data(self) -> NetDataReader:
67
+ """Get additional data from connection request."""
68
+ return self._internal_packet.data
69
+
70
+ @property
71
+ def remote_address(self) -> Tuple[str, int]:
72
+ """Get remote endpoint address."""
73
+ return self._remote_address
74
+
75
+ @property
76
+ def connection_time(self) -> int:
77
+ """Get connection timestamp from request."""
78
+ return self._internal_packet.connection_time
79
+
80
+ @property
81
+ def connection_number(self) -> int:
82
+ """Get connection number from request."""
83
+ return self._internal_packet.connection_number
84
+
85
+ @property
86
+ def peer_id(self) -> int:
87
+ """Get peer ID from request."""
88
+ return self._internal_packet.peer_id
89
+
90
+ @property
91
+ def result(self) -> int:
92
+ """Get request result (after accept/reject)."""
93
+ return self._result
94
+
95
+ def update_request(self, connect_request: NetConnectRequestPacket) -> None:
96
+ """
97
+ Update request with newer connection attempt.
98
+
99
+ Only updates if the new request is newer (based on timestamp and connection number).
100
+
101
+ Args:
102
+ connect_request: New connection request packet
103
+
104
+ C# Equivalent: UpdateRequest(NetConnectRequestPacket)
105
+ """
106
+ # Old request - ignore
107
+ if connect_request.connection_time < self._internal_packet.connection_time:
108
+ return
109
+
110
+ # Same request - ignore
111
+ if (connect_request.connection_time == self._internal_packet.connection_time and
112
+ connect_request.connection_number == self._internal_packet.connection_number):
113
+ return
114
+
115
+ # Newer request - update
116
+ self._internal_packet = connect_request
117
+
118
+ def _try_activate(self) -> bool:
119
+ """
120
+ Try to activate request for processing (thread-safe).
121
+
122
+ Returns:
123
+ True if successfully activated (first time), False if already used
124
+
125
+ C# Equivalent: TryActivate()
126
+ Uses Interlocked.CompareExchange for thread safety
127
+ """
128
+ # Python doesn't have true atomic operations, but threading.Lock is sufficient
129
+ with threading.Lock():
130
+ if self._used == 0:
131
+ self._used = 1
132
+ return True
133
+ return False
134
+
135
+ def accept_if_key(self, key: str) -> Optional:
136
+ """
137
+ Accept connection only if data matches key.
138
+
139
+ Args:
140
+ key: Expected key string in connection data
141
+
142
+ Returns:
143
+ Connected peer if key matches, None otherwise
144
+
145
+ C# Equivalent: AcceptIfKey(string key)
146
+ """
147
+ if not self._try_activate():
148
+ return None
149
+
150
+ try:
151
+ reader = NetDataReader(self._internal_packet.data)
152
+ received_key = reader.get_string()
153
+ if received_key == key:
154
+ self._result = ConnectionRequestResult.ACCEPT
155
+ except Exception:
156
+ # NetDebug.WriteError("[AC] Invalid incoming data")
157
+ pass
158
+
159
+ if self._result == ConnectionRequestResult.ACCEPT:
160
+ return self._manager.on_connection_solved(self, None, 0, 0)
161
+
162
+ self._result = ConnectionRequestResult.REJECT
163
+ self._manager.on_connection_solved(self, None, 0, 0)
164
+ return None
165
+
166
+ def accept(self) -> Optional:
167
+ """
168
+ Accept connection and get connected peer.
169
+
170
+ Returns:
171
+ Connected peer, or None if request already processed
172
+
173
+ C# Equivalent: Accept()
174
+ """
175
+ if not self._try_activate():
176
+ return None
177
+
178
+ self._result = ConnectionRequestResult.ACCEPT
179
+ return self._manager.on_connection_solved(self, None, 0, 0)
180
+
181
+ def reject(
182
+ self,
183
+ reject_data: Optional[bytes] = None,
184
+ start: int = 0,
185
+ length: Optional[int] = None,
186
+ force: bool = False
187
+ ) -> None:
188
+ """
189
+ Reject connection with optional data.
190
+
191
+ Args:
192
+ reject_data: Optional rejection data bytes
193
+ start: Starting offset in reject_data
194
+ length: Number of bytes from reject_data to send
195
+ force: Whether to force rejection
196
+
197
+ C# Equivalent: Reject(byte[], int, int, bool)
198
+ """
199
+ if not self._try_activate():
200
+ return
201
+
202
+ if length is None and reject_data is not None:
203
+ length = len(reject_data)
204
+
205
+ self._result = ConnectionRequestResult.REJECT_FORCE if force else ConnectionRequestResult.REJECT
206
+ self._manager.on_connection_solved(self, reject_data, start, length if length else 0)
207
+
208
+ def reject_force(
209
+ self,
210
+ reject_data: Optional[bytes] = None,
211
+ start: int = 0,
212
+ length: Optional[int] = None
213
+ ) -> None:
214
+ """
215
+ Force reject connection.
216
+
217
+ Args:
218
+ reject_data: Optional rejection data bytes
219
+ start: Starting offset in reject_data
220
+ length: Number of bytes from reject_data to send
221
+
222
+ C# Equivalent: RejectForce(byte[], int, int)
223
+ """
224
+ self.reject(reject_data, start, length, force=True)
225
+
226
+ def reject_with_writer(self, reject_data: NetDataWriter, force: bool = False) -> None:
227
+ """
228
+ Reject connection with NetDataWriter.
229
+
230
+ Args:
231
+ reject_data: Data writer with rejection information
232
+ force: Whether to force rejection
233
+
234
+ C# Equivalent: Reject(NetDataWriter) or RejectForce(NetDataWriter)
235
+ """
236
+ self.reject(reject_data.data, 0, reject_data.length, force)
237
+
238
+ def __repr__(self) -> str:
239
+ return (f"ConnectionRequest(remote={self._remote_address}, "
240
+ f"time={self.connection_time}, peer_id={self.peer_id})")