easyprotolib 0.1.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,86 @@
1
+ debug = False
2
+
3
+
4
+ from .basic_datatypes import MCObject
5
+
6
+ from .basic_datatypes import MCBoolean
7
+ from .basic_datatypes import MCByte, MCUnsignedByte
8
+ from .basic_datatypes import MCShort, MCUnsignedShort
9
+ from .basic_datatypes import MCInt, MCLong
10
+ from .basic_datatypes import MCFloat, MCDouble
11
+ from .basic_datatypes import MCVarInt, MCVarLong
12
+ from .basic_datatypes import MCAngle, MCUUID, MCLpVec3, MCPosition
13
+ from .basic_datatypes import MCString, MCIdentifier, MCJSONTextComponent
14
+
15
+ from .basic_datatypes import MCObjectArray
16
+ from .basic_datatypes import MCByteArray, MCUnsignedByteArray
17
+ from .basic_datatypes import MCLongArray
18
+ from .basic_datatypes import MCVarIntArray
19
+ from .basic_datatypes import MCAngleArray
20
+ from .basic_datatypes import MCIdentifierArray
21
+ from .basic_datatypes import MCUnsignedByteArrayArray
22
+
23
+ from .basic_datatypes import MCBaseBytearray, MCVarBaseBytearray
24
+ from .basic_datatypes import MCDependentObject, MCStruct
25
+
26
+ from .basic_datatypes import MCObjectSetter, MCObjectDuplicator
27
+
28
+
29
+ from .nbt import MCNBT, TAGArray
30
+
31
+ from .nbt import TAGEnd
32
+ from .nbt import TAGByte, TAGShort, TAGInt, TAGLong, TAGFloat, TAGDouble
33
+ from .nbt import TAGByteArray
34
+ from .nbt import TAGString
35
+ from .nbt import TAGList
36
+ from .nbt import TAGCompound
37
+ from .nbt import TAGIntArray, TAGLongArray
38
+
39
+
40
+ from .advanced_datatypes import MCBitSet
41
+ from .advanced_datatypes import MCPaletteContainer, MCBiomePaletteContainer, MCBlockPaletteContainer
42
+ from .advanced_datatypes import MCProtocolChunkSection
43
+ from .advanced_datatypes import MCChunkData, MCLightData, MCHeightMap
44
+
45
+
46
+ from .data_packet import MCDataPackets
47
+ from .data_packet import MCConfig
48
+ from .data_packet import S2C, C2S
49
+ from .data_packet import SIDE_CLIENT, SIDE_SERVER
50
+ from .data_packet import STATE_HANDSHAKE, STATE_STATE, STATE_LOGIN, STATE_PLAY, STATE_CONFIGURATION
51
+
52
+ from .data_packet import MCDataPacket
53
+ from .data_packet import MCCDataPacket, MCSDataPacket
54
+ from .data_packet import MCCStateDataPacket, MCCLoginDataPacket, MCCPlayDataPacket, MCCConfigurationDataPacket
55
+ from .data_packet import MCSHandshakeDataPacket, MCSStateDataPacket, MCSLoginDataPacket, MCSPlayDataPacket, MCSConfigurationDataPacket
56
+
57
+ from .data_packet import MCSHandshake, MCSLegacyServerListPing # State 0
58
+ from .data_packet import MCSRequestStatus, MCSPingStatus # State 1
59
+ from .data_packet import MCSLoginStart, MCSEncryptionResponse, MCSLoginPluginResponse # State 2
60
+ from .data_packet import (MCSTeleportConfirm, MCSSetDifficulty, MCSChatMessage, MCSClientStatus, MCSClientSettings,
61
+ MCSPluginMessage, MCSKeepAlive, MCSPlayerPosition, MCSPlayerPositionAndRotation, MCSPongPlay) # State 3
62
+
63
+ from .data_packet import MCCResponseStatus, MCCPongStatus # State 1
64
+ from .data_packet import MCCDisconnectLogin, MCCEncryptionRequest, MCCLoginSuccess, MCCSetCompression, MCCLoginPluginRequest # State 2
65
+ from .data_packet import (MCCServerDifficulty, MCCChatMessage, MCCDeclareCommands, MCCPluginMessage, MCCDisconnectPlay,
66
+ MCCUnloadChunk, MCCKeepAlive, MCCChunkDataAndUpdateLight, MCCUpdateLight, MCCJoinGame,
67
+ MCCOpenBook, MCCPingPlay, MCCHeldItemChange, MCCTimeUpdate) # State 3
68
+
69
+
70
+ from .block import MCBlock
71
+ from .block import has_direction, can_contain_water, full_block, air, liquid
72
+
73
+
74
+ from .biome import MCBiome
75
+
76
+
77
+ from .chunk import MCChunk, MCChunkSection
78
+
79
+
80
+ from .command import MCCommandGraph
81
+
82
+ from .command import (MCCommandLiteralNode, MCCommandArgumentNode, MCCommandIsExecutable,
83
+ MCCommandHasRedirect, MCCommandHasSuggestionsType)
84
+
85
+
86
+ from .err import MinecraftException, MCProtocolIdNotFound
@@ -0,0 +1,459 @@
1
+ import sys
2
+ from typing import Any
3
+
4
+ from .basic_datatypes import MCObject, MCObjectArray, MCVarInt, MCLongArray, MCUnsignedByteArrayArray, MCUnsignedByte, MCShort, MCVarIntArray, MCInt, MCLong
5
+ from .nbt import TAGLongArray, TAGCompound, MCNBT
6
+ from .block import MCBlock, MCBlockEntitiesMap
7
+ import math
8
+ import time
9
+ import array
10
+
11
+
12
+ def _pack(l: list[int], bpe: int) -> list[int]:
13
+ if bpe < 1 or bpe > 64:
14
+ raise ValueError(f"BPE({bpe}) must be between 1 and 64")
15
+ n = len(l)
16
+ if bpe == 4:
17
+ num_longs = (n + 15) >> 4
18
+ result = [0] * num_longs
19
+ for i in range(n):
20
+ long_idx = i >> 4
21
+ bit_idx = (i & 15) << 2
22
+ # 清空目标位
23
+ result[long_idx] &= ~(15 << bit_idx)
24
+ # 写入新值
25
+ result[long_idx] |= l[i] << bit_idx
26
+ else:
27
+ entries_per_long = 64 // bpe
28
+ num_longs = (n + entries_per_long - 1) // entries_per_long
29
+ result = [0] * num_longs
30
+ mask = (1 << bpe) - 1
31
+ for i in range(n):
32
+ long_idx = i // entries_per_long
33
+ bit_idx = (i % entries_per_long) * bpe
34
+ # 清空目标位
35
+ result[long_idx] &= ~(mask << bit_idx)
36
+ # 写入新值
37
+ result[long_idx] |= l[i] << bit_idx
38
+ threshold = 1 << 63
39
+ mask = 1 << 64
40
+ # 返回的列表中整数的取值范围为0 ~ (1 << 64) - 1
41
+ return [x - mask if x >= threshold else x for x in result] # 最后一步: 80~100μs
42
+
43
+
44
+ def _pack_to_bytes_fast(l: list[int], bpe: int) -> bytearray | bytes:
45
+ if bpe < 1 or bpe > 64:
46
+ raise ValueError(f"BPE({bpe}) must be between 1 and 64")
47
+ n = len(l)
48
+ assert n & 7 == 0
49
+ if bpe == 1:
50
+ num = 0
51
+ for bit in l:
52
+ num = (num << 1) | bit
53
+ result = num.to_bytes(n >> 3, 'big')
54
+ # 可以保证n >> 3 << 3 == n
55
+ return MCVarInt(((n >> 3) + 7) >> 3) + result + b'\x00' * ((8 - ((n >> 3) & 7)) & 7)
56
+ elif bpe == 4:
57
+ num_longs = (n + 15) >> 4 << 3
58
+ result = bytearray(num_longs)
59
+ for i in range(0, n, 2):
60
+ result[i >> 1] = (l[i] << 4) | l[i + 1]
61
+ # 可以保证不存在奇数尾巴
62
+ return MCVarInt(num_longs >> 3) + result
63
+ elif bpe == 8:
64
+ return MCVarInt(n >> 3) + bytes(l)
65
+ else:
66
+ entries_per_long = 64 // bpe
67
+ num_longs = (n + entries_per_long - 1) // entries_per_long
68
+ result = [0] * num_longs
69
+ for i in range(n):
70
+ long_idx = i // entries_per_long
71
+ bit_idx = (i % entries_per_long) * bpe
72
+ # 写入新值
73
+ result[long_idx] |= l[i] << bit_idx
74
+ result = array.array("Q", result)
75
+ if sys.byteorder != "big":
76
+ result.byteswap()
77
+ return MCVarInt(num_longs) + result.tobytes()
78
+
79
+
80
+ def _unpack(l: list[int], bpe: int, length: int) -> list[int]:
81
+ if bpe < 1 or bpe > 64:
82
+ raise ValueError(f"BPE({bpe}) must be between 1 and 64")
83
+ entries_per_long = 64 // bpe
84
+ mask = (1 << bpe) - 1
85
+ result = [0] * length
86
+ for i in range(length):
87
+ long_idx = i // entries_per_long
88
+ bit_idx = (i % entries_per_long) * bpe
89
+ result[i] = (l[long_idx] >> bit_idx) & mask
90
+ return result
91
+
92
+
93
+ class MCBitSet(MCLongArray):
94
+ def __init__(self, data: list[bool] | tuple[bool]):
95
+ n = len(data)
96
+ if n == 0:
97
+ super().__init__([])
98
+ return
99
+ num_longs = (n + 0x3f) >> 6
100
+ result = [0] * num_longs
101
+ for i in range(num_longs):
102
+ start = i << 6
103
+ end = min(start + 0x40, n)
104
+ val = 0
105
+ for j in range(start, end):
106
+ if data[j]:
107
+ val |= (1 << (j - start))
108
+ result[i] = val
109
+ super().__init__(result)
110
+
111
+ @classmethod
112
+ def _obj_deserialization(cls, data: bytearray, bit_count=-1) -> tuple[list[bool], int]:
113
+ length, offset = MCVarInt._obj_deserialization(data)
114
+ longs = []
115
+ for i in range(length):
116
+ longs.append(MCLong._obj_deserialization(data[offset:offset + 8])[0])
117
+ offset += 8
118
+ bits = [False] * bit_count
119
+ for i in range(bit_count):
120
+ long_idx = i >> 6
121
+ bit_offset = i & 0x3f
122
+ if long_idx < len(longs):
123
+ bit = (longs[long_idx] >> bit_offset) & 1
124
+ bits[i] = bit == 1
125
+ return bits, offset
126
+
127
+
128
+ # noinspection DuplicatedCode
129
+ class MCLightData(MCObject):
130
+
131
+ def __init__(self, sky_light: list[int], block_light: list[int], section_count: int=24, fast_mode=False):
132
+ super().__init__((sky_light, block_light, section_count))
133
+ self.fast_mode = fast_mode
134
+
135
+ def _obj_serialization(self) -> bytearray:
136
+ sky_light_, block_light_, section_count = self.data
137
+
138
+ total_sections = section_count + 2
139
+ # 初始化掩码
140
+ sky_mask = [True] * total_sections
141
+ block_mask = [True] * total_sections
142
+
143
+ assert len(sky_light_) == len(block_light_)
144
+
145
+ if self.fast_mode:
146
+ empty_sky_mask = [False] * total_sections
147
+ empty_block_mask = [False] * total_sections
148
+ sky_arrays = [[(sky_light_[j + 1] << 4) | sky_light_[j] for j in range(i << 1, (i << 1) + 4096, 2)] for i in range(0, total_sections << 11, 2048)]
149
+ block_arrays = [[(block_light_[j + 1] << 4) | block_light_[j] for j in range(i << 1, (i << 1) + 4096, 2)] for i in range(0, total_sections << 11, 2048)]
150
+ # 假设光照数据确实严格在0~15范围内
151
+ # -5~256 在 CPython 中是永生对象, 不会重复创建和销毁
152
+ else:
153
+ empty_sky_mask = [True] * total_sections
154
+ empty_block_mask = [True] * total_sections
155
+ l = 0
156
+ sky_arrays = []
157
+ block_arrays = []
158
+ for i in range(0, len(sky_light_), 2):
159
+ a = l >> 11
160
+ byte_val = ((sky_light_[i + 1] & 0x0F) << 4) | (sky_light_[i] & 0x0F) # 高4位放第二个,低4位放第一个
161
+ if byte_val != 0:
162
+ empty_sky_mask[a] = False
163
+ try:
164
+ sky_arrays[a].append(byte_val)
165
+ except IndexError:
166
+ sky_arrays.append([byte_val])
167
+
168
+ byte_val = ((block_light_[i + 1] & 0x0F) << 4) | (block_light_[i] & 0x0F) # 高4位放第二个,低4位放第一个
169
+ if byte_val != 0:
170
+ empty_block_mask[a] = False
171
+ try:
172
+ block_arrays[a].append(byte_val)
173
+ except IndexError:
174
+ block_arrays.append([byte_val])
175
+ l += 1
176
+
177
+ result = MCBitSet(sky_mask).serialization()
178
+ result += MCBitSet(block_mask)
179
+ result += MCBitSet(empty_sky_mask)
180
+ result += MCBitSet(empty_block_mask)
181
+ try:
182
+ result += MCUnsignedByteArrayArray(sky_arrays)
183
+ result += MCUnsignedByteArrayArray(block_arrays)
184
+ except OverflowError as e:
185
+ if self.fast_mode:
186
+ print("部分光照强度可能不在0~15之内, 快速模式关闭! 这是预期中的吗? ")
187
+ self.fast_mode = False
188
+ return self._obj_serialization()
189
+ raise e
190
+ # result 的组装耗时: 100~130ms -> 1~2ms
191
+ return result # 方法总耗时: 240~350ms -> 200~250ms -> 130~160ms -> 30~50ms -> 15~22ms(FAST MODE) -> 13~15ms(FAST MODE)
192
+
193
+ @classmethod
194
+ def _obj_deserialization(cls, data: bytearray, section_count: int=24) -> tuple[tuple[list[int], list[int]], int]:
195
+ sky_mask, offset = MCBitSet._obj_deserialization(data, section_count + 2)
196
+ block_mask, l = MCBitSet._obj_deserialization(data[offset:], section_count + 2)
197
+ offset += l
198
+ empty_sky_mask, l = MCBitSet._obj_deserialization(data[offset:], section_count + 2)
199
+ offset += l
200
+ empty_block_mask, l = MCBitSet._obj_deserialization(data[offset:], section_count + 2)
201
+ offset += l
202
+ sky_arrays, l = MCUnsignedByteArrayArray._obj_deserialization(data[offset:])
203
+ offset += l
204
+ block_arrays, l = MCUnsignedByteArrayArray._obj_deserialization(data[offset:])
205
+ offset += l
206
+
207
+ # 验证长度一致
208
+ if not (len(block_mask) == len(empty_sky_mask) == len(empty_block_mask) == len(sky_mask)):
209
+ raise ValueError("所有掩码的长度必须相同")
210
+
211
+ sky_light = []
212
+ block_light = []
213
+
214
+ sky_idx = 0
215
+ block_idx = 0
216
+
217
+ for i in range(len(sky_mask)):
218
+ if sky_mask[i]:
219
+ if sky_idx >= len(sky_arrays):
220
+ raise IndexError(f"sky_arrays 不足:期望第 {sky_idx} 段,但只有 {len(sky_arrays)} 段")
221
+ compressed_sky = sky_arrays[sky_idx]
222
+ sky_idx += 1
223
+ if empty_sky_mask[i]:
224
+ sky_light.extend([0] * 4096)
225
+ else:
226
+ sky_light.extend(cls.decompress_light_section(compressed_sky))
227
+ else:
228
+ sky_light.extend([0] * 4096)
229
+ if block_mask[i]:
230
+ if block_idx >= len(block_arrays):
231
+ raise IndexError(f"block_arrays 不足:期望第 {block_idx} 段,但只有 {len(block_arrays)} 段")
232
+ compressed_block = block_arrays[block_idx]
233
+ block_idx += 1
234
+
235
+ if empty_block_mask[i]:
236
+ block_light.extend([0] * 4096)
237
+ else:
238
+ block_light.extend(cls.decompress_light_section(compressed_block))
239
+ else:
240
+ block_light.extend([0] * 4096)
241
+ if sky_idx != len(sky_arrays):
242
+ raise ValueError(f"sky_arrays 未完全消耗:期望 {len(sky_arrays)} 段,实际消耗 {sky_idx} 段")
243
+ if block_idx != len(block_arrays):
244
+ raise ValueError(f"block_arrays 未完全消耗:期望 {len(block_arrays)} 段,实际消耗 {block_idx} 段")
245
+
246
+ return (sky_light, block_light), offset
247
+
248
+ @staticmethod
249
+ def decompress_light_section(compressed: list[int]) -> list[int]:
250
+ if len(compressed) != 2048:
251
+ raise ValueError("每个子区块的压缩数据必须为 2048 字节")
252
+
253
+ result = [0] * 4096
254
+ for i, byte_val in enumerate(compressed):
255
+ result[2 * i] = byte_val & 0x0F # 低4位
256
+ result[2 * i + 1] = (byte_val >> 4) & 0x0F # 高4位
257
+ return result
258
+
259
+
260
+ class MCHeightMap(MCObject):
261
+ world_height = -1
262
+
263
+ def __init__(self, heightmap: dict[str, list[int]]):
264
+ """heightmap 期望的y坐标是已经减去世界最低坐标的偏移值"""
265
+ # 高度图在高版本不再是NBT了, 但在1.18.2中, 高度图仍然是NBT
266
+ if self.world_height == -1:
267
+ raise ValueError("请设置正确的 world_height 值")
268
+ super().__init__(heightmap)
269
+
270
+ def _obj_serialization(self) -> bytearray:
271
+ heightmap_ = self.data
272
+ world_height = self.world_height
273
+ bits_per_entry = math.ceil(math.log2(world_height + 1))
274
+ result = []
275
+ for name in heightmap_:
276
+ heightmap = heightmap_[name]
277
+ if len(heightmap) != 256:
278
+ raise ValueError("高度图必须恰好包含256个条目(16*16)")
279
+ result.append(TAGLongArray(name, _pack(heightmap, bits_per_entry)))
280
+ return TAGCompound("", result).serialization() # 0~1ms
281
+
282
+ @classmethod
283
+ def _obj_deserialization(cls, data: bytearray) -> tuple[dict[str, list[int]], int]:
284
+ bits_per_entry = math.ceil(math.log2(cls.world_height + 1))
285
+ result = MCNBT._obj_deserialization(data)
286
+ result2 = {result[0][2][0].name: _unpack(result[0][2][0].data, bits_per_entry, 256), result[0][2][1].name: _unpack(
287
+ result[0][2][1].data, bits_per_entry, 256)}
288
+ return result2, result[1]
289
+
290
+
291
+ class MCPaletteContainer(MCObject):
292
+ min_bpe = -1 # 间接模式最小BPE
293
+ max_bpe = -1 # 间接模式最大BPE
294
+ bpe = -1 # 直接模式最小BPE
295
+ length = -1
296
+ def __init__(self, data: list[int]):
297
+ # data中存放全局id
298
+ super().__init__(data)
299
+
300
+ def _obj_serialization(self) -> bytearray:
301
+ result = bytearray(b'')
302
+ i = list(set(self.data))
303
+ if len(i) == 1:
304
+ bpe = 0
305
+ else:
306
+ bpe = max(self.min_bpe, math.ceil(math.log2(len(i))))
307
+ if bpe > self.max_bpe:
308
+ # 直接模式
309
+ bpe = max(self.bpe, bpe)
310
+ result += MCUnsignedByte(bpe)
311
+ result += _pack_to_bytes_fast(self.data, bpe) # 在1.21.5+, 这个数组不带长度前缀, 不过我们实现的协议版本是1.18.2
312
+ elif bpe > 0:
313
+ # 间接模式
314
+ result += MCUnsignedByte(bpe)
315
+ result += MCVarIntArray(i)
316
+ if len(i) > 20:
317
+ i = {k: j for j, k in enumerate(i)}
318
+ data = [i[j] for j in self.data]
319
+ else:
320
+ data = [i.index(j) for j in self.data] # 190~220μs
321
+ result += _pack_to_bytes_fast(data, bpe) # 1.8~2.1ms -> 1.6~1.8ms -> 330~500μs
322
+ else:
323
+ # 单值模式
324
+ result += MCUnsignedByte(0)
325
+ result += MCVarInt(i[0])
326
+ result += MCInt(0) # 我不知道为什么要加上这个MCInt(0), 这也不是协议里的标准内容, 但是加上这个之后, 程序就能跑了!(什么鬼...)
327
+ return result
328
+
329
+ @classmethod
330
+ def _obj_deserialization(cls, data: bytearray) -> tuple[list[int], int]:
331
+ bpe, offset = MCUnsignedByte._obj_deserialization(data)
332
+ if bpe >= cls.bpe:
333
+ result, l = MCLongArray._obj_deserialization(data[offset:])
334
+ offset += l
335
+ result = _unpack(result, bpe, cls.length)
336
+ elif bpe == 0:
337
+ result, l = MCVarInt._obj_deserialization(data[offset:])
338
+ offset += l
339
+ _, l = MCInt._obj_deserialization(data[offset:])
340
+ offset += l
341
+ result = [result] * cls.length
342
+ elif cls.min_bpe <= bpe <= cls.max_bpe:
343
+ i, l = MCVarIntArray._obj_deserialization(data[offset:])
344
+ offset += l
345
+ result, l = MCLongArray._obj_deserialization(data[offset:])
346
+ offset += l
347
+ result = _unpack(result, bpe, cls.length)
348
+ result = [i[j] for j in result]
349
+ else:
350
+ raise ValueError("BPE不正确")
351
+ return result, offset
352
+
353
+
354
+ class MCBlockPaletteContainer(MCPaletteContainer):
355
+ min_bpe = 4 # 间接模式最小BPE
356
+ max_bpe = 8 # 间接模式最大BPE
357
+ bpe = 15 # 直接模式最小BPE
358
+ length = 4096 # 一旦正确配置了length, 该类型的反序列化方法就能正常运作, 但我懒得配置了(), 以后补上:D (归档)但已于2026/7/23补上
359
+
360
+
361
+ class MCBiomePaletteContainer(MCPaletteContainer):
362
+ min_bpe = 1 # 间接模式最小BPE
363
+ max_bpe = 3 # 间接模式最大BPE
364
+ bpe = 7 # 直接模式最小BPE
365
+ length = 64
366
+
367
+
368
+ class MCProtocolChunkSection(MCObject):
369
+ def __init__(self, cs):
370
+ super().__init__(cs)
371
+
372
+ def _obj_serialization(self) -> bytearray:
373
+ result = bytearray(b'')
374
+ cs = self.data
375
+ result += MCShort(cs.get_block_count() - cs.get_air_count()) # 1~2ms -> 0~1ms -> 7~8μs -> 3~5μs
376
+ result += MCBlockPaletteContainer(cs.blocks_id) # 5~6ms -> 4~5ms -> 1.6~2.0ms
377
+ result += MCBiomePaletteContainer(cs.biomes_id) # 0ms -> 100~120μs -> 20~24μs
378
+ return result # 11~15ms -> 4~7ms -> 3~4ms -> 2.5~3.0ms -> 1.7~2.0ms
379
+
380
+ @staticmethod
381
+ def _obj_deserialization(data: bytearray) -> tuple[tuple[int, list[int], list[int]], int]:
382
+ block_count, offset = MCShort.deserialization(data)
383
+ block_palette, l = MCBlockPaletteContainer.deserialization(data[offset:])
384
+ offset += l
385
+ biome_palette, l = MCBiomePaletteContainer.deserialization(data[offset:])
386
+ offset += l
387
+ return (block_count, block_palette, biome_palette), offset
388
+
389
+
390
+ class MCChunkData(MCObject):
391
+ def __init__(self, chunk):
392
+ super().__init__(chunk)
393
+
394
+ def _obj_serialization(self) -> bytearray:
395
+ chunk = self.data
396
+ result = bytearray(b'')
397
+ for i in chunk.chunk_sections:
398
+ result += MCProtocolChunkSection(i) # 300~360ms -> 140~180ms -> 2ms
399
+ return MCVarInt(len(result)) + result
400
+
401
+ @staticmethod
402
+ def _obj_deserialization(data: bytearray) -> tuple[list[tuple[int, list[int], list[int]]], int]:
403
+ length, offset = MCVarInt.deserialization(data)
404
+ offset2 = 0
405
+ result = []
406
+ while offset2 < length:
407
+ pcs, l = MCProtocolChunkSection.deserialization(data)
408
+ result.append(pcs)
409
+ offset2 += l
410
+ return result, offset + offset2
411
+
412
+
413
+ class MCBlockEntity(MCObject):
414
+ def __init__(self, block: MCBlock):
415
+ if not block.is_block_entity:
416
+ raise ValueError("方块不是方块实体")
417
+ super().__init__(block)
418
+
419
+ def _obj_serialization(self) -> bytearray:
420
+ block: MCBlock = self.data
421
+ result = MCUnsignedByte(((block.x & 0x0f) << 4) | (block.z & 0x0f)).serialization()
422
+ result += MCShort(block.y)
423
+ result += MCVarInt(block.block_entity_id)
424
+ result += block.block_entity_data
425
+ return result
426
+
427
+ @staticmethod
428
+ def _obj_deserialization(data: bytearray) -> tuple[MCBlock, int]:
429
+ packed_xz, offset = MCUnsignedByte.deserialization(data)
430
+ x, z = packed_xz >> 4, packed_xz & 0x0f
431
+ y, l = MCShort.deserialization(data[offset:])
432
+ offset += l
433
+ id, l = MCVarInt.deserialization(data[offset:])
434
+ offset += l
435
+ nbt_data, l = MCNBT.deserialization(data[offset:])
436
+ offset += l
437
+ block: MCBlock = MCBlockEntitiesMap.get(id)()
438
+ block._set_block_pos(x, z)
439
+ block._set_y(y)
440
+ block.block_entity_data = TAGCompound("", nbt_data)
441
+ return block, offset
442
+
443
+
444
+ class MCBlockEntities(MCObjectArray):
445
+ MCObjectType = MCBlockEntity
446
+
447
+
448
+ if __name__ == "__main__":
449
+ data1 = [0] * (4096 * 26)
450
+ for i in range(1024):
451
+ data1[i] = 15
452
+
453
+ data2 = [0] * (4096 * 26)
454
+ for i in range(1024):
455
+ data2[i] = 15
456
+
457
+ t = time.perf_counter()
458
+ a = MCLightData(data1, data2).serialization()
459
+ print(time.perf_counter()-t)