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,402 @@
1
+ """
2
+ Network data writer for serializing data to binary format.
3
+
4
+ Provides methods to write various data types in a compact binary format
5
+ compatible with LiteNetLib protocol.
6
+ """
7
+
8
+ import struct
9
+ import uuid
10
+ from typing import Union, Tuple, List, Optional
11
+
12
+
13
+ class NetDataWriter:
14
+ """
15
+ Binary data writer with auto-resizing buffer.
16
+
17
+ Uses little-endian byte order for multi-byte values.
18
+ All data is written as compactly as possible.
19
+ """
20
+
21
+ __slots__ = ('_data', '_position', '_auto_resize', '_initial_size')
22
+
23
+ def __init__(self, auto_resize: bool = True, initial_size: int = 64):
24
+ """
25
+ Create a new NetDataWriter.
26
+
27
+ Args:
28
+ auto_resize: Automatically resize buffer when needed
29
+ initial_size: Initial buffer size in bytes
30
+ """
31
+ self._data = bytearray(initial_size)
32
+ self._position = 0
33
+ self._auto_resize = auto_resize
34
+ self._initial_size = initial_size
35
+
36
+ @classmethod
37
+ def from_bytes(cls, data: bytes, copy: bool = True) -> 'NetDataWriter':
38
+ """
39
+ Create NetDataWriter from existing bytes.
40
+
41
+ Args:
42
+ data: Source bytes
43
+ copy: Whether to copy the data or reuse the buffer
44
+ """
45
+ writer = cls(auto_resize=True, initial_size=len(data) if copy else 0)
46
+ if copy:
47
+ writer._data = bytearray(data)
48
+ writer._position = len(data)
49
+ else:
50
+ writer._data = bytearray(data) # Still need to copy as bytearray
51
+ writer._position = len(data)
52
+ return writer
53
+
54
+ @classmethod
55
+ def from_string(cls, value: str) -> 'NetDataWriter':
56
+ """Create NetDataWriter from a string."""
57
+ writer = cls()
58
+ writer.put(value)
59
+ return writer
60
+
61
+ @property
62
+ def capacity(self) -> int:
63
+ """Get current buffer capacity."""
64
+ return len(self._data)
65
+
66
+ @property
67
+ def data(self) -> bytearray:
68
+ """Get underlying data buffer."""
69
+ return self._data
70
+
71
+ @property
72
+ def length(self) -> int:
73
+ """Get current data length (position)."""
74
+ return self._position
75
+
76
+ def reset(self, size: Optional[int] = None) -> None:
77
+ """
78
+ Reset writer position.
79
+
80
+ Args:
81
+ size: If provided, ensure buffer is at least this size
82
+ """
83
+ if size is not None:
84
+ self._ensure_capacity(size)
85
+ self._position = 0
86
+
87
+ def set_position(self, position: int) -> int:
88
+ """
89
+ Set writer position.
90
+
91
+ Args:
92
+ position: New position
93
+
94
+ Returns:
95
+ Previous position
96
+ """
97
+ prev = self._position
98
+ self._position = position
99
+ return prev
100
+
101
+ def copy_data(self) -> bytes:
102
+ """Copy written data to new bytes object."""
103
+ return bytes(self._data[:self._position])
104
+
105
+ def to_bytes(self) -> bytes:
106
+ """Get written data as bytes."""
107
+ return bytes(self._data[:self._position])
108
+
109
+ def _ensure_capacity(self, size: int) -> None:
110
+ """Ensure buffer has enough capacity."""
111
+ if len(self._data) < size:
112
+ new_size = max(size, len(self._data) * 2)
113
+ self._data.extend(b'\x00' * (new_size - len(self._data)))
114
+
115
+ def _resize_if_needed(self, additional_size: int) -> None:
116
+ """Resize buffer if needed for additional data."""
117
+ if self._auto_resize:
118
+ self._ensure_capacity(self._position + additional_size)
119
+
120
+ # Basic types
121
+
122
+ def put_byte(self, value: int) -> None:
123
+ """Write a single byte."""
124
+ self._resize_if_needed(1)
125
+ self._data[self._position] = value & 0xFF
126
+ self._position += 1
127
+
128
+ def put_sbyte(self, value: int) -> None:
129
+ """Write a signed byte."""
130
+ self.put_byte(value & 0xFF)
131
+
132
+ def put_bool(self, value: bool) -> None:
133
+ """Write a boolean as a single byte (0 or 1)."""
134
+ self.put_byte(1 if value else 0)
135
+
136
+ def put_short(self, value: int) -> None:
137
+ """Write a 16-bit signed integer."""
138
+ self._resize_if_needed(2)
139
+ struct.pack_into('<h', self._data, self._position, value)
140
+ self._position += 2
141
+
142
+ def put_ushort(self, value: int) -> None:
143
+ """Write a 16-bit unsigned integer."""
144
+ self._resize_if_needed(2)
145
+ struct.pack_into('<H', self._data, self._position, value)
146
+ self._position += 2
147
+
148
+ def put_int(self, value: int) -> None:
149
+ """Write a 32-bit signed integer."""
150
+ self._resize_if_needed(4)
151
+ struct.pack_into('<i', self._data, self._position, value)
152
+ self._position += 4
153
+
154
+ def put_uint(self, value: int) -> None:
155
+ """Write a 32-bit unsigned integer."""
156
+ self._resize_if_needed(4)
157
+ struct.pack_into('<I', self._data, self._position, value)
158
+ self._position += 4
159
+
160
+ def put_long(self, value: int) -> None:
161
+ """Write a 64-bit signed integer."""
162
+ self._resize_if_needed(8)
163
+ struct.pack_into('<q', self._data, self._position, value)
164
+ self._position += 8
165
+
166
+ def put_ulong(self, value: int) -> None:
167
+ """Write a 64-bit unsigned integer."""
168
+ self._resize_if_needed(8)
169
+ struct.pack_into('<Q', self._data, self._position, value)
170
+ self._position += 8
171
+
172
+ def put_float(self, value: float) -> None:
173
+ """Write a 32-bit float."""
174
+ self._resize_if_needed(4)
175
+ struct.pack_into('<f', self._data, self._position, value)
176
+ self._position += 4
177
+
178
+ def put_double(self, value: float) -> None:
179
+ """Write a 64-bit double."""
180
+ self._resize_if_needed(8)
181
+ struct.pack_into('<d', self._data, self._position, value)
182
+ self._position += 8
183
+
184
+ def put_char(self, value: str) -> None:
185
+ """Write a single character as ushort."""
186
+ self.put_ushort(ord(value[0]) if len(value) > 0 else 0)
187
+
188
+ # String types
189
+
190
+ def put_string(self, value: str, max_length: int = 0) -> None:
191
+ """
192
+ Write a string with length prefix.
193
+
194
+ Args:
195
+ value: String to write
196
+ max_length: Maximum character length (0 = no limit)
197
+ """
198
+ if not value:
199
+ self.put_ushort(0)
200
+ return
201
+
202
+ # Truncate if max_length specified
203
+ if max_length > 0 and len(value) > max_length:
204
+ value = value[:max_length]
205
+
206
+ encoded = value.encode('utf-8')
207
+ size = len(encoded)
208
+
209
+ if size == 0:
210
+ self.put_ushort(0)
211
+ return
212
+
213
+ self.put_ushort(size + 1) # Size + 1 for non-empty string
214
+ self._resize_if_needed(size)
215
+ self._data[self._position:self._position + size] = encoded
216
+ self._position += size
217
+
218
+ def put_large_string(self, value: str) -> None:
219
+ """
220
+ Write a potentially large string with int length prefix.
221
+
222
+ Args:
223
+ value: String to write
224
+ """
225
+ if not value:
226
+ self.put_int(0)
227
+ return
228
+
229
+ encoded = value.encode('utf-8')
230
+ size = len(encoded)
231
+
232
+ if size == 0:
233
+ self.put_int(0)
234
+ return
235
+
236
+ self.put_int(size)
237
+ self._resize_if_needed(size)
238
+ self._data[self._position:self._position + size] = encoded
239
+ self._position += size
240
+
241
+ # Bytes and arrays
242
+
243
+ def put_bytes(self, data: bytes, offset: int = 0, length: Optional[int] = None) -> None:
244
+ """
245
+ Write raw bytes.
246
+
247
+ Args:
248
+ data: Bytes to write
249
+ offset: Starting offset in data
250
+ length: Number of bytes to write (None = all remaining)
251
+ """
252
+ if length is None:
253
+ length = len(data) - offset
254
+ self._resize_if_needed(length)
255
+ self._data[self._position:self._position + length] = data[offset:offset + length]
256
+ self._position += length
257
+
258
+ def put_bytes_with_length(self, data: bytes) -> None:
259
+ """Write bytes with ushort length prefix."""
260
+ length = len(data)
261
+ self.put_ushort(length)
262
+ self.put_bytes(data)
263
+
264
+ def put_array(self, arr: Optional[List], element_size: int = 1) -> None:
265
+ """
266
+ Write an array with length prefix.
267
+
268
+ Args:
269
+ arr: Array to write (None writes length 0)
270
+ element_size: Size of each element in bytes
271
+ """
272
+ length = len(arr) if arr is not None else 0
273
+ total_size = length * element_size
274
+ self._resize_if_needed(2 + total_size)
275
+ self.put_ushort(length)
276
+
277
+ if arr and total_size > 0:
278
+ # Handle different element types
279
+ if element_size == 1:
280
+ # bytes or bool or small integers
281
+ for item in arr:
282
+ if isinstance(item, bool):
283
+ self.put_byte(1 if item else 0)
284
+ else:
285
+ # Preserve integer values (0-255)
286
+ self.put_byte(item & 0xFF)
287
+ elif element_size == 2:
288
+ # shorts or ushorts
289
+ for item in arr:
290
+ self.put_short(item) if isinstance(item, int) and item < 0 else self.put_ushort(item)
291
+ elif element_size == 4:
292
+ # ints or floats
293
+ for item in arr:
294
+ if isinstance(item, float):
295
+ self.put_float(item)
296
+ else:
297
+ self.put_int(item)
298
+ elif element_size == 8:
299
+ # longs, ulongs, or doubles
300
+ for item in arr:
301
+ if isinstance(item, float):
302
+ self.put_double(item)
303
+ else:
304
+ self.put_long(item)
305
+
306
+ def put_string_array(self, arr: Optional[List[str]], max_length: int = 0) -> None:
307
+ """
308
+ Write an array of strings.
309
+
310
+ Args:
311
+ arr: Array of strings (None writes length 0)
312
+ max_length: Maximum length for each string
313
+ """
314
+ length = len(arr) if arr is not None else 0
315
+ self.put_ushort(length)
316
+
317
+ if arr:
318
+ for s in arr:
319
+ self.put_string(s, max_length)
320
+
321
+ # Special types
322
+
323
+ def put_uuid(self, value: uuid.UUID) -> None:
324
+ """Write a UUID (16 bytes)."""
325
+ self._resize_if_needed(16)
326
+ self._data[self._position:self._position + 16] = value.bytes
327
+ self._position += 16
328
+
329
+ def put_ip_end_point(self, address: str, port: int, family: str = 'IPv4') -> None:
330
+ """
331
+ Write an IP endpoint.
332
+
333
+ Args:
334
+ address: IP address string
335
+ port: Port number
336
+ family: 'IPv4' or 'IPv6'
337
+ """
338
+ import socket
339
+
340
+ # Normalize family string to handle both 'IPv4' and 'IPV4'
341
+ family_upper = family.upper()
342
+ if family_upper == 'IPV4':
343
+ family_upper = 'IPv4'
344
+ elif family_upper == 'IPV6':
345
+ family_upper = 'IPv6'
346
+
347
+ if family_upper == 'IPv4':
348
+ self.put_byte(0)
349
+ addr_bytes = socket.inet_pton(socket.AF_INET, address)
350
+ elif family_upper == 'IPv6':
351
+ self.put_byte(1)
352
+ addr_bytes = socket.inet_pton(socket.AF_INET6, address)
353
+ else:
354
+ raise ValueError(f"Unsupported address family: {family}")
355
+
356
+ self.put_bytes(addr_bytes)
357
+ self.put_ushort(port)
358
+
359
+ # Convenience methods
360
+
361
+ def put(self, value) -> None:
362
+ """
363
+ Write a value based on its type.
364
+
365
+ This is a convenience method that automatically selects
366
+ the appropriate put_* method based on the value type.
367
+ """
368
+ if isinstance(value, bool):
369
+ self.put_bool(value)
370
+ elif isinstance(value, int):
371
+ # Determine best integer size
372
+ if -128 <= value <= 127:
373
+ self.put_sbyte(value)
374
+ elif -32768 <= value <= 32767:
375
+ self.put_short(value)
376
+ elif -2147483648 <= value <= 2147483647:
377
+ self.put_int(value)
378
+ else:
379
+ self.put_long(value)
380
+ elif isinstance(value, float):
381
+ self.put_float(value)
382
+ elif isinstance(value, str):
383
+ self.put_string(value)
384
+ elif isinstance(value, bytes):
385
+ self.put_bytes_with_length(value)
386
+ elif isinstance(value, (list, tuple)):
387
+ # Try to determine element type and size
388
+ if value and isinstance(value[0], str):
389
+ self.put_string_array(list(value))
390
+ else:
391
+ # Default to int array
392
+ self.put_array(list(value), 4)
393
+ elif isinstance(value, uuid.UUID):
394
+ self.put_uuid(value)
395
+ else:
396
+ raise TypeError(f"Unsupported type: {type(value)}")
397
+
398
+ def __len__(self) -> int:
399
+ return self._position
400
+
401
+ def __repr__(self) -> str:
402
+ return f"NetDataWriter(position={self._position}, capacity={len(self._data)})"
@@ -0,0 +1,199 @@
1
+ """
2
+ Fast binary converter for exact C# compatibility / 快速二进制转换器,与 C# 完全兼容
3
+
4
+ This module provides little-endian binary encoding that exactly matches
5
+ the C# FastBitConverter implementation. All multi-byte values use
6
+ little-endian byte order for protocol compatibility.
7
+
8
+ 本模块提供与 C# FastBitConverter 实现完全匹配的小端二进制编码。
9
+ 所有多字节值使用小端字节序以确保协议兼容性。
10
+
11
+ Ported from: LiteNetLib/Utils/FastBitConverter.cs
12
+ """
13
+
14
+ import struct
15
+ from typing import Union
16
+
17
+
18
+ class FastBitConverter:
19
+ """
20
+ Fast little-endian binary converter / 快速小端二进制转换器
21
+
22
+ Provides methods to write primitive types to byte arrays in little-endian
23
+ format, exactly matching the C# implementation for protocol compatibility.
24
+
25
+ 提供将基本类型以小端格式写入字节数组的方法,与 C# 实现完全匹配。
26
+
27
+ C# Reference: FastBitConverter.GetBytes(byte[], int, T value)
28
+ """
29
+
30
+ __slots__ = ()
31
+
32
+ @staticmethod
33
+ def get_bytes(buffer: bytearray, offset: int, value: int) -> None:
34
+ """
35
+ Write integer (32-bit) to buffer at offset.
36
+ 写入 32 位整数到缓冲区指定位置。
37
+
38
+ Args:
39
+ buffer: Target byte array / 目标字节数组
40
+ offset: Starting position in buffer / 缓冲区起始位置
41
+ value: Integer value to write / 要写入的整数值
42
+
43
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, int value)
44
+ """
45
+ struct.pack_into('<i', buffer, offset, value)
46
+
47
+ @staticmethod
48
+ def get_bytes_uint(buffer: bytearray, offset: int, value: int) -> None:
49
+ """
50
+ Write unsigned integer (32-bit) to buffer at offset.
51
+ 写入 32 位无符号整数到缓冲区指定位置。
52
+
53
+ Args:
54
+ buffer: Target byte array / 目标字节数组
55
+ offset: Starting position in buffer / 缓冲区起始位置
56
+ value: Unsigned integer value to write / 要写入的无符号整数值
57
+
58
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, uint value)
59
+ """
60
+ struct.pack_into('<I', buffer, offset, value)
61
+
62
+ @staticmethod
63
+ def get_bytes_ushort(buffer: bytearray, offset: int, value: int) -> None:
64
+ """
65
+ Write unsigned short (16-bit) to buffer at offset.
66
+ 写入 16 位无符号短整数到缓冲区指定位置。
67
+
68
+ Args:
69
+ buffer: Target byte array / 目标字节数组
70
+ offset: Starting position in buffer / 缓冲区起始位置
71
+ value: Unsigned short value to write / 要写入的无符号短整数值
72
+
73
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, ushort value)
74
+ """
75
+ struct.pack_into('<H', buffer, offset, value)
76
+
77
+ @staticmethod
78
+ def get_bytes_short(buffer: bytearray, offset: int, value: int) -> None:
79
+ """
80
+ Write short (16-bit) to buffer at offset.
81
+ 写入 16 位短整数到缓冲区指定位置。
82
+
83
+ Args:
84
+ buffer: Target byte array / 目标字节数组
85
+ offset: Starting position in buffer / 缓冲区起始位置
86
+ value: Short value to write / 要写入的短整数值
87
+
88
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, short value)
89
+ """
90
+ struct.pack_into('<h', buffer, offset, value)
91
+
92
+ @staticmethod
93
+ def get_bytes_long(buffer: bytearray, offset: int, value: int) -> None:
94
+ """
95
+ Write long (64-bit) to buffer at offset.
96
+ 写入 64 位长整数到缓冲区指定位置。
97
+
98
+ Args:
99
+ buffer: Target byte array / 目标字节数组
100
+ offset: Starting position in buffer / 缓冲区起始位置
101
+ value: Long value to write / 要写入的长整数值
102
+
103
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, long value)
104
+ """
105
+ struct.pack_into('<q', buffer, offset, value)
106
+
107
+ @staticmethod
108
+ def get_bytes_ulong(buffer: bytearray, offset: int, value: int) -> None:
109
+ """
110
+ Write unsigned long (64-bit) to buffer at offset.
111
+ 写入 64 位无符号长整数到缓冲区指定位置。
112
+
113
+ Args:
114
+ buffer: Target byte array / 目标字节数组
115
+ offset: Starting position in buffer / 缓冲区起始位置
116
+ value: Unsigned long value to write / 要写入的无符号长整数值
117
+
118
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, ulong value)
119
+ """
120
+ struct.pack_into('<Q', buffer, offset, value)
121
+
122
+ @staticmethod
123
+ def get_bytes_float(buffer: bytearray, offset: int, value: float) -> None:
124
+ """
125
+ Write float (32-bit IEEE 754) to buffer at offset.
126
+ 写入 32 位 IEEE 754 浮点数到缓冲区指定位置。
127
+
128
+ Args:
129
+ buffer: Target byte array / 目标字节数组
130
+ offset: Starting position in buffer / 缓冲区起始位置
131
+ value: Float value to write / 要写入的浮点数值
132
+
133
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, float value)
134
+
135
+ Note: Uses struct.pack with '<f' for exact IEEE 754 binary representation
136
+ matching C# float encoding.
137
+ 注意:使用 struct.pack 的 '<f' 格式以实现与 C# float 编码完全匹配的 IEEE 754 二进制表示。
138
+ """
139
+ struct.pack_into('<f', buffer, offset, value)
140
+
141
+ @staticmethod
142
+ def get_bytes_double(buffer: bytearray, offset: int, value: float) -> None:
143
+ """
144
+ Write double (64-bit IEEE 754) to buffer at offset.
145
+ 写入 64 位 IEEE 754 双精度浮点数到缓冲区指定位置。
146
+
147
+ Args:
148
+ buffer: Target byte array / 目标字节数组
149
+ offset: Starting position in buffer / 缓冲区起始位置
150
+ value: Double value to write / 要写入的双精度浮点数值
151
+
152
+ C# Equivalent: GetBytes(byte[] bytes, int startIndex, double value)
153
+
154
+ Note: Uses struct.pack with '<d' for exact IEEE 754 binary representation
155
+ matching C# double encoding.
156
+ 注意:使用 struct.pack 的 '<d' 格式以实现与 C# double 编码完全匹配的 IEEE 754 二进制表示。
157
+ """
158
+ struct.pack_into('<d', buffer, offset, value)
159
+
160
+
161
+ # Convenience functions / 便捷函数
162
+ def write_int16(buffer: bytearray, offset: int, value: int) -> None:
163
+ """Write signed 16-bit integer / 写入有符号 16 位整数"""
164
+ FastBitConverter.get_bytes_short(buffer, offset, value)
165
+
166
+
167
+ def write_uint16(buffer: bytearray, offset: int, value: int) -> None:
168
+ """Write unsigned 16-bit integer / 写入无符号 16 位整数"""
169
+ FastBitConverter.get_bytes_ushort(buffer, offset, value)
170
+
171
+
172
+ def write_int32(buffer: bytearray, offset: int, value: int) -> None:
173
+ """Write signed 32-bit integer / 写入有符号 32 位整数"""
174
+ FastBitConverter.get_bytes(buffer, offset, value)
175
+
176
+
177
+ def write_uint32(buffer: bytearray, offset: int, value: int) -> None:
178
+ """Write unsigned 32-bit integer / 写入无符号 32 位整数"""
179
+ FastBitConverter.get_bytes_uint(buffer, offset, value)
180
+
181
+
182
+ def write_int64(buffer: bytearray, offset: int, value: int) -> None:
183
+ """Write signed 64-bit integer / 写入有符号 64 位整数"""
184
+ FastBitConverter.get_bytes_long(buffer, offset, value)
185
+
186
+
187
+ def write_uint64(buffer: bytearray, offset: int, value: int) -> None:
188
+ """Write unsigned 64-bit integer / 写入无符号 64 位整数"""
189
+ FastBitConverter.get_bytes_ulong(buffer, offset, value)
190
+
191
+
192
+ def write_float32(buffer: bytearray, offset: int, value: float) -> None:
193
+ """Write 32-bit float / 写入 32 位浮点数"""
194
+ FastBitConverter.get_bytes_float(buffer, offset, value)
195
+
196
+
197
+ def write_float64(buffer: bytearray, offset: int, value: float) -> None:
198
+ """Write 64-bit double / 写入 64 位双精度浮点数"""
199
+ FastBitConverter.get_bytes_double(buffer, offset, value)