Bug Summary

File:Protocol.c
Warning:line 1176, column 18
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple amd64-unknown-openbsd6.8 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name Protocol.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mthread-model posix -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/10.0.1 -I /usr/X11R6/include -I /usr/local/include -fdebug-compilation-dir /home/ben/Projects/ClassiCube/src -ferror-limit 19 -fmessage-length 0 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fobjc-runtime=gnustep -fdiagnostics-show-option -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/ClassiCube/src/scan/2021-01-09-173753-31878-1 -x c Protocol.c
1#include "Protocol.h"
2#include "String.h"
3#include "Deflate.h"
4#include "Server.h"
5#include "Stream.h"
6#include "Game.h"
7#include "Entity.h"
8#include "Platform.h"
9#include "Screens.h"
10#include "World.h"
11#include "Event.h"
12#include "ExtMath.h"
13#include "SelectionBox.h"
14#include "Chat.h"
15#include "Inventory.h"
16#include "Block.h"
17#include "Model.h"
18#include "Funcs.h"
19#include "Lighting.h"
20#include "Http.h"
21#include "Drawer2D.h"
22#include "Logger.h"
23#include "TexturePack.h"
24#include "Gui.h"
25#include "Errors.h"
26#include "Camera.h"
27#include "Window.h"
28#include "Particle.h"
29#include "Picking.h"
30#include "Input.h"
31#include "Utils.h"
32
33#define QUOTE(x)"x" #x
34#define STRINGIFY(val)"val" QUOTE(val)"val"
35struct _ProtocolData Protocol;
36
37/* Classic state */
38static cc_uint8 classic_tabList[ENTITIES_MAX_COUNT256 >> 3];
39static cc_bool classic_receivedFirstPos;
40
41/* Map state */
42static cc_bool map_begunLoading;
43static cc_uint64 map_receiveBeg;
44static struct Stream map_part;
45static struct GZipHeader map_gzHeader;
46static int map_sizeIndex, map_volume;
47static cc_uint8 map_size[4];
48
49struct MapState {
50 struct InflateState inflateState;
51 struct Stream stream;
52 BlockRaw* blocks;
53 int index;
54 cc_bool allocFailed;
55};
56static struct MapState map;
57#ifdef EXTENDED_BLOCKS
58static struct MapState map2;
59#endif
60
61/* CPE state */
62cc_bool cpe_needD3Fix;
63static int cpe_serverExtensionsCount, cpe_pingTicks;
64static int cpe_envMapVer = 2, cpe_blockDefsExtVer = 2, cpe_customModelsVer = 2;
65static cc_bool cpe_sendHeldBlock, cpe_useMessageTypes, cpe_extEntityPos, cpe_blockPerms, cpe_fastMap;
66static cc_bool cpe_twoWayPing, cpe_extTextures, cpe_extBlocks;
67
68/*########################################################################################################################*
69*-----------------------------------------------------Common handlers-----------------------------------------------------*
70*#########################################################################################################################*/
71#define Classic_TabList_Get(id)(classic_tabList[id >> 3] & (1 << (id & 0x7
)))
(classic_tabList[id >> 3] & (1 << (id & 0x7)))
72#define Classic_TabList_Set(id)(classic_tabList[id >> 3] |= (cc_uint8)(1 << (id &
0x7)))
(classic_tabList[id >> 3] |= (cc_uint8)(1 << (id & 0x7)))
73#define Classic_TabList_Reset(id)(classic_tabList[id >> 3] &= (cc_uint8)~(1 <<
(id & 0x7)))
(classic_tabList[id >> 3] &= (cc_uint8)~(1 << (id & 0x7)))
74
75#ifndef EXTENDED_BLOCKS
76#define ReadBlock(data, value)if (cpe_extBlocks) { value = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { value = *data++; }
value = *data++;
77#else
78#define ReadBlock(data, value)if (cpe_extBlocks) { value = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { value = *data++; }
\
79if (cpe_extBlocks) {\
80 value = Stream_GetU16_BE(data) % BLOCK_COUNT; data += 2;\
81} else { value = *data++; }
82#endif
83
84#ifndef EXTENDED_BLOCKS
85#define WriteBlock(data, value)if (cpe_extBlocks) { Stream_SetU16_BE(data, value); data += 2
;} else { *data++ = (BlockRaw)value; }
*data++ = value;
86#else
87#define WriteBlock(data, value)if (cpe_extBlocks) { Stream_SetU16_BE(data, value); data += 2
;} else { *data++ = (BlockRaw)value; }
\
88if (cpe_extBlocks) {\
89 Stream_SetU16_BE(data, value); data += 2;\
90} else { *data++ = (BlockRaw)value; }
91#endif
92
93static cc_string UNSAFE_GetString(cc_uint8* data) {
94 int i, length = 0;
95 for (i = STRING_SIZE64 - 1; i >= 0; i--) {
96 char code = data[i];
97 if (code == '\0' || code == ' ') continue;
98 length = i + 1; break;
99 }
100 return String_Init((char*)data, length, STRING_SIZE64);
101}
102
103static float GetFloat(cc_uint8* data) {
104 union IntAndFloat raw;
105 raw.u = Stream_GetU32_BE(data);
106 return raw.f;
107}
108
109static void ReadString(cc_uint8** ptr, cc_string* str) {
110 int i, length = 0;
111 cc_uint8* data = *ptr;
112 for (i = STRING_SIZE64 - 1; i >= 0; i--) {
113 char code = data[i];
114 if (code == '\0' || code == ' ') continue;
115 length = i + 1; break;
116 }
117
118 String_AppendAll(str, data, length);
119 *ptr = data + STRING_SIZE64;
120}
121
122static void WriteString(cc_uint8* data, const cc_string* value) {
123 int i, count = min(value->length, STRING_SIZE)((value->length) < (64) ? (value->length) : (64));
124 for (i = 0; i < count; i++) {
125 char c = value->buffer[i];
126 if (c == '&') c = '%'; /* escape colour codes */
127 data[i] = c;
128 }
129
130 for (; i < STRING_SIZE64; i++) { data[i] = ' '; }
131}
132
133static void RemoveEndPlus(cc_string* value) {
134 /* Workaround for MCDzienny (and others) use a '+' at the end to distinguish classicube.net accounts */
135 /* from minecraft.net accounts. Unfortunately they also send this ending + to the client. */
136 if (!value->length || value->buffer[value->length - 1] != '+') return;
137 value->length--;
138}
139
140static void AddTablistEntry(EntityID id, const cc_string* playerName, const cc_string* listName, const cc_string* groupName, cc_uint8 groupRank) {
141 cc_string rawName; char rawBuffer[STRING_SIZE64];
142 String_InitArray(rawName, rawBuffer)rawName.buffer = rawBuffer; rawName.length = 0; rawName.capacity
= sizeof(rawBuffer);
;
143
144 String_AppendColorless(&rawName, playerName);
145 TabList_Set(id, &rawName, listName, groupName, groupRank);
146}
147
148static void CheckName(EntityID id, cc_string* name, cc_string* skin) {
149 cc_string colorlessName; char colorlessBuffer[STRING_SIZE64];
150
151 RemoveEndPlus(name);
152 /* Server is only allowed to change our own name colours. */
153 if (id == ENTITIES_SELF_ID255) {
154 String_InitArray(colorlessName, colorlessBuffer)colorlessName.buffer = colorlessBuffer; colorlessName.length =
0; colorlessName.capacity = sizeof(colorlessBuffer);
;
155 String_AppendColorless(&colorlessName, name);
156 if (!String_Equals(&colorlessName, &Game_Username)) String_Copy(name, &Game_Username);
157 }
158
159 if (!skin->length) String_Copy(skin, name);
160 RemoveEndPlus(skin);
161}
162
163static void Classic_ReadAbsoluteLocation(cc_uint8* data, EntityID id, cc_bool interpolate);
164static void AddEntity(cc_uint8* data, EntityID id, const cc_string* name, const cc_string* skin, cc_bool readPosition) {
165 struct LocalPlayer* p = &LocalPlayer_Instance;
166 struct Entity* e;
167
168 if (id != ENTITIES_SELF_ID255) {
169 if (Entities.List[id]) Entities_Remove(id);
170 e = &NetPlayers_List[id].Base;
171
172 NetPlayer_Init((struct NetPlayer*)e);
173 Entities.List[id] = e;
174 Event_RaiseInt(&EntityEvents.Added, id);
175 } else {
176 e = &LocalPlayer_Instance.Base;
177 }
178 Entity_SetSkin(e, skin);
179 Entity_SetName(e, name);
180
181 if (!readPosition) return;
182 Classic_ReadAbsoluteLocation(data, id, false0);
183 if (id != ENTITIES_SELF_ID255) return;
184
185 p->Spawn = p->Base.Position;
186 p->SpawnYaw = p->Base.Yaw;
187 p->SpawnPitch = p->Base.Pitch;
188}
189
190void Protocol_RemoveEntity(EntityID id) {
191 struct Entity* e = Entities.List[id];
192 if (!e) return;
193 if (id != ENTITIES_SELF_ID255) Entities_Remove(id);
194
195 /* See comment about some servers in Classic_AddEntity */
196 if (!Classic_TabList_Get(id)(classic_tabList[id >> 3] & (1 << (id & 0x7
)))
) return;
197 TabList_Remove(id);
198 Classic_TabList_Reset(id)(classic_tabList[id >> 3] &= (cc_uint8)~(1 <<
(id & 0x7)))
;
199}
200
201static void UpdateLocation(EntityID id, struct LocationUpdate* update, cc_bool interpolate) {
202 struct Entity* e = Entities.List[id];
203 if (e) { e->VTABLE->SetLocation(e, update, interpolate); }
204}
205
206static void UpdateUserType(struct HacksComp* hacks, cc_uint8 value) {
207 cc_bool isOp = value >= 100 && value <= 127;
208 hacks->IsOp = isOp;
209 if (cpe_blockPerms) return;
210
211 Blocks.CanPlace[BLOCK_BEDROCK] = isOp;
212 Blocks.CanDelete[BLOCK_BEDROCK] = isOp;
213 Blocks.CanPlace[BLOCK_WATER] = isOp;
214 Blocks.CanPlace[BLOCK_STILL_WATER] = isOp;
215 Blocks.CanPlace[BLOCK_LAVA] = isOp;
216 Blocks.CanPlace[BLOCK_STILL_LAVA] = isOp;
217}
218
219
220/*########################################################################################################################*
221*------------------------------------------------------WoM protocol-------------------------------------------------------*
222*#########################################################################################################################*/
223/* Partially based on information from http://files.worldofminecraft.com/texturing/ */
224/* NOTE: http://files.worldofminecraft.com/ has been down for quite a while, so support was removed on Oct 10, 2015 */
225static int wom_identifier;
226static cc_bool wom_sendId, wom_sentId;
227
228static void WoM_CheckMotd(void) {
229 cc_string url; char urlBuffer[STRING_SIZE64];
230 cc_string motd, host;
231 int index;
232
233 motd = Server.MOTD;
234 if (!motd.length) return;
235 index = String_IndexOfConst(&motd, "cfg=");
236 if (Game_PureClassic(Game_ClassicMode && !Game_ClassicHacks) || index == -1) return;
237
238 host = String_UNSAFE_SubstringAt(&motd, index + 4);
239 String_InitArray(url, urlBuffer)url.buffer = urlBuffer; url.length = 0; url.capacity = sizeof
(urlBuffer);
;
240 String_Format1(&url, "http://%s", &host);
241 /* TODO: Replace $U with username */
242 /*url = url.Replace("$U", game.Username); */
243
244 /* Ensure that if the user quickly changes to a different world, env settings from old world aren't
245 applied in the new world if the async 'get env request' didn't complete before the old world was unloaded */
246 wom_identifier = Http_AsyncGetData(&url, true1);
247 wom_sendId = true1;
248}
249
250static void WoM_CheckSendWomID(void) {
251 static const cc_string msg = String_FromConst("/womid WoMClient-2.0.7"){ "/womid WoMClient-2.0.7", (sizeof("/womid WoMClient-2.0.7")
- 1), (sizeof("/womid WoMClient-2.0.7") - 1)}
;
252
253 if (wom_sendId && !wom_sentId) {
254 Chat_Send(&msg, false0);
255 wom_sentId = true1;
256 }
257}
258
259static PackedCol WoM_ParseCol(const cc_string* value, PackedCol defaultCol) {
260 int argb;
261 if (!Convert_ParseInt(value, &argb)) return defaultCol;
262 return PackedCol_Make(argb >> 16, argb >> 8, argb, 255)(((cc_uint8)(argb >> 16) << 0) | ((cc_uint8)(argb
>> 8) << 8) | ((cc_uint8)(argb) << 16) | (
(cc_uint8)(255) << 24))
;
263}
264
265static void WoM_ParseConfig(struct HttpRequest* item) {
266 cc_string line; char lineBuffer[STRING_SIZE64 * 2];
267 struct Stream mem;
268 cc_string key, value;
269 int waterLevel;
270 PackedCol col;
271
272 String_InitArray(line, lineBuffer)line.buffer = lineBuffer; line.length = 0; line.capacity = sizeof
(lineBuffer);
;
273 Stream_ReadonlyMemory(&mem, item->data, item->size);
274
275 while (!Stream_ReadLine(&mem, &line)) {
276 Platform_Log(line.buffer, line.length);
277 if (!String_UNSAFE_Separate(&line, '=', &key, &value)) continue;
278
279 if (String_CaselessEqualsConst(&key, "environment.cloud")) {
280 col = WoM_ParseCol(&value, ENV_DEFAULT_CLOUDS_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
);
281 Env_SetCloudsCol(col);
282 } else if (String_CaselessEqualsConst(&key, "environment.sky")) {
283 col = WoM_ParseCol(&value, ENV_DEFAULT_SKY_COL(((cc_uint8)(0x99) << 0) | ((cc_uint8)(0xCC) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
);
284 Env_SetSkyCol(col);
285 } else if (String_CaselessEqualsConst(&key, "environment.fog")) {
286 col = WoM_ParseCol(&value, ENV_DEFAULT_FOG_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
);
287 Env_SetFogCol(col);
288 } else if (String_CaselessEqualsConst(&key, "environment.level")) {
289 if (Convert_ParseInt(&value, &waterLevel)) {
290 Env_SetEdgeHeight(waterLevel);
291 }
292 } else if (String_CaselessEqualsConst(&key, "user.detail") && !cpe_useMessageTypes) {
293 Chat_AddOf(&value, MSG_TYPE_STATUS_2);
294 }
295 }
296}
297
298static void WoM_Reset(void) {
299 wom_identifier = 0;
300 wom_sendId = false0; wom_sentId = false0;
301}
302
303static void WoM_Tick(void) {
304 struct HttpRequest item;
305 if (!Http_GetResult(wom_identifier, &item)) return;
306 if (!item.success) return;
307
308 WoM_ParseConfig(&item);
309 Mem_Free(item.data);
310}
311
312
313/*########################################################################################################################*
314*----------------------------------------------------Classic protocol-----------------------------------------------------*
315*#########################################################################################################################*/
316
317void Classic_SendChat(const cc_string* text, cc_bool partial) {
318 cc_uint8 data[66];
319 data[0] = OPCODE_MESSAGE;
320 {
321 data[1] = Server.SupportsPartialMessages ? partial : ENTITIES_SELF_ID255;
322 WriteString(&data[2], text);
323 }
324 Server.SendData(data, 66);
325}
326
327void Classic_WritePosition(Vec3 pos, float yaw, float pitch) {
328 BlockID payload;
329 int x, y, z;
330
331 cc_uint8* data = Server.WriteBuffer;
332 *data++ = OPCODE_ENTITY_TELEPORT;
333 {
334 payload = cpe_sendHeldBlock ? Inventory_SelectedBlock(Inventory.Table[Inventory.Offset + (Inventory.SelectedIndex)
])
: ENTITIES_SELF_ID255;
335 WriteBlock(data, payload)if (cpe_extBlocks) { Stream_SetU16_BE(data, payload); data +=
2;} else { *data++ = (BlockRaw)payload; }
;
336 x = (int)(pos.X * 32);
337 y = (int)(pos.Y * 32) + 51;
338 z = (int)(pos.Z * 32);
339
340 if (cpe_extEntityPos) {
341 Stream_SetU32_BE(data, x); data += 4;
342 Stream_SetU32_BE(data, y); data += 4;
343 Stream_SetU32_BE(data, z); data += 4;
344 } else {
345 Stream_SetU16_BE(data, x); data += 2;
346 Stream_SetU16_BE(data, y); data += 2;
347 Stream_SetU16_BE(data, z); data += 2;
348 }
349
350 *data++ = Math_Deg2Packed(yaw)((cc_uint8)((yaw) * 256.0f / 360.0f));
351 *data++ = Math_Deg2Packed(pitch)((cc_uint8)((pitch) * 256.0f / 360.0f));
352 }
353 Server.WriteBuffer = data;
354}
355
356void Classic_WriteSetBlock(int x, int y, int z, cc_bool place, BlockID block) {
357 cc_uint8* data = Server.WriteBuffer;
358 *data++ = OPCODE_SET_BLOCK_CLIENT;
359 {
360 Stream_SetU16_BE(data, x); data += 2;
361 Stream_SetU16_BE(data, y); data += 2;
362 Stream_SetU16_BE(data, z); data += 2;
363 *data++ = place;
364 WriteBlock(data, block)if (cpe_extBlocks) { Stream_SetU16_BE(data, block); data += 2
;} else { *data++ = (BlockRaw)block; }
;
365 }
366 Server.WriteBuffer = data;
367}
368
369void Classic_SendLogin(void) {
370 cc_uint8 data[131];
371 data[0] = OPCODE_HANDSHAKE;
372 {
373 data[1] = 7; /* protocol version */
374 WriteString(&data[2], &Game_Username);
375 WriteString(&data[66], &Game_Mppass);
376 data[130] = Game_UseCPE ? 0x42 : 0x00;
377 }
378 Server.SendData(data, 131);
379}
380
381static void Classic_Handshake(cc_uint8* data) {
382 struct HacksComp* hacks;
383
384 Server.Name.length = 0;
385 Server.MOTD.length = 0;
386 data++; /* protocol version */
387
388 ReadString(&data, &Server.Name);
389 ReadString(&data, &Server.MOTD);
390 Chat_SetLogName(&Server.Name);
391
392 hacks = &LocalPlayer_Instance.Hacks;
393 UpdateUserType(hacks, *data);
394
395 String_Copy(&hacks->HacksFlags, &Server.Name);
396 String_AppendString(&hacks->HacksFlags, &Server.MOTD);
397 HacksComp_RecheckFlags(hacks);
398}
399
400static void Classic_Ping(cc_uint8* data) { }
401
402#define MAP_SIZE_LEN4 4
403static void DisconnectInvalidMap(cc_result res) {
404 static const cc_string title = String_FromConst("Disconnected"){ "Disconnected", (sizeof("Disconnected") - 1), (sizeof("Disconnected"
) - 1)}
;
405 cc_string tmp; char tmpBuffer[STRING_SIZE64];
406 String_InitArray(tmp, tmpBuffer)tmp.buffer = tmpBuffer; tmp.length = 0; tmp.capacity = sizeof
(tmpBuffer);
;
407
408 String_Format1(&tmp, "Server sent corrupted map data (error %h)", &res);
409 Game_Disconnect(&title, &tmp); return;
410}
411
412static void MapState_Init(struct MapState* m) {
413 Inflate_MakeStream2(&m->stream, &m->inflateState, &map_part);
414 m->index = 0;
415 m->blocks = NULL((void*)0);
416 m->allocFailed = false0;
417}
418
419static void FreeMapStates(void) {
420 Mem_Free(map.blocks);
421 map.blocks = NULL((void*)0);
422#ifdef EXTENDED_BLOCKS
423 Mem_Free(map2.blocks);
424 map2.blocks = NULL((void*)0);
425#endif
426}
427
428static void MapState_Read(struct MapState* m) {
429 cc_uint32 left, read;
430 cc_result res;
431 if (m->allocFailed) return;
432
433 if (!m->blocks) {
434 m->blocks = (BlockRaw*)Mem_TryAlloc(map_volume, 1);
435 /* unlikely but possible */
436 if (!m->blocks) {
437 Window_ShowDialog("Out of memory", "Not enough free memory to join that map.\nTry joining a different map.");
438 m->allocFailed = true1;
439 return;
440 }
441 }
442
443 left = map_volume - m->index;
444 res = m->stream.Read(&m->stream, &m->blocks[m->index], left, &read);
445
446 if (res) DisconnectInvalidMap(res);
447 m->index += read;
448}
449
450static void Classic_StartLoading(void) {
451 World_NewMap();
452 Stream_ReadonlyMemory(&map_part, NULL((void*)0), 0);
453
454 LoadingScreen_Show(&Server.Name, &Server.MOTD);
455 WoM_CheckMotd();
456 classic_receivedFirstPos = false0;
457
458 GZipHeader_Init(&map_gzHeader);
459 map_begunLoading = true1;
460 map_sizeIndex = 0;
461 map_receiveBeg = Stopwatch_Measure();
462 map_volume = 0;
463
464 MapState_Init(&map);
465#ifdef EXTENDED_BLOCKS
466 MapState_Init(&map2);
467#endif
468}
469
470static void Classic_LevelInit(cc_uint8* data) {
471 if (!map_begunLoading) Classic_StartLoading();
472 if (!cpe_fastMap) return;
473
474 /* Fast map puts volume in header, and uses raw DEFLATE without GZIP header/footer */
475 map_volume = Stream_GetU32_BE(data);
476 map_gzHeader.done = true1;
477 map_sizeIndex = MAP_SIZE_LEN4;
478}
479
480static void Classic_LevelDataChunk(cc_uint8* data) {
481 int usedLength;
482 float progress;
483 cc_uint32 left, read;
484 cc_uint8 value;
485 cc_result res;
486
487 /* Workaround for some servers that send LevelDataChunk before LevelInit due to their async sending behaviour */
488 if (!map_begunLoading) Classic_StartLoading();
489 usedLength = Stream_GetU16_BE(data); data += 2;
490
491 map_part.Meta.Mem.Cur = data;
492 map_part.Meta.Mem.Base = data;
493 map_part.Meta.Mem.Left = usedLength;
494 map_part.Meta.Mem.Length = usedLength;
495
496 data += 1024;
497 value = *data; /* progress in original classic, but we ignore it */
498
499 if (!map_gzHeader.done) {
500 res = GZipHeader_Read(&map_part, &map_gzHeader);
501 if (res && res != ERR_END_OF_STREAM) { DisconnectInvalidMap(res); return; }
502 }
503
504 if (map_gzHeader.done) {
505 if (map_sizeIndex < MAP_SIZE_LEN4) {
506 left = MAP_SIZE_LEN4 - map_sizeIndex;
507 res = map.stream.Read(&map.stream, &map_size[map_sizeIndex], left, &read);
508
509 if (res) { DisconnectInvalidMap(res); return; }
510 map_sizeIndex += read;
511 }
512
513 if (map_sizeIndex == MAP_SIZE_LEN4) {
514 if (!map_volume) map_volume = Stream_GetU32_BE(map_size);
515
516#ifndef EXTENDED_BLOCKS
517 MapState_Read(&map);
518#else
519 if (cpe_extBlocks && value) {
520 MapState_Read(&map2);
521 } else {
522 MapState_Read(&map);
523 }
524#endif
525 }
526 }
527
528 progress = !map.blocks ? 0.0f : (float)map.index / map_volume;
529 Event_RaiseFloat(&WorldEvents.Loading, progress);
530}
531
532static void Classic_LevelFinalise(cc_uint8* data) {
533 int width, height, length;
534 cc_uint64 end;
535 int delta;
536
537 end = Stopwatch_Measure();
538 delta = Stopwatch_ElapsedMS(map_receiveBeg, end);
539 Platform_Log1("map loading took: %i", &delta);
540 map_begunLoading = false0;
541 WoM_CheckSendWomID();
542
543#ifdef EXTENDED_BLOCKS
544 if (map2.allocFailed) FreeMapStates();
545#endif
546
547 width = Stream_GetU16_BE(data + 0);
548 height = Stream_GetU16_BE(data + 2);
549 length = Stream_GetU16_BE(data + 4);
550
551 if (map_volume != (width * height * length)) {
552 Chat_AddRaw("&cFailed to load map, try joining a different map");
553 Chat_AddRaw(" &cBlocks array size does not match volume of map");
554 FreeMapStates();
555 }
556
557#ifdef EXTENDED_BLOCKS
558 /* defer allocation of second map array if possible */
559 if (cpe_extBlocks && map2.blocks) World_SetMapUpper(map2.blocks);
560 map2.blocks = NULL((void*)0);
561#endif
562 World_SetNewMap(map.blocks, width, height, length);
563 map.blocks = NULL((void*)0);
564}
565
566static void Classic_SetBlock(cc_uint8* data) {
567 int x, y, z;
568 BlockID block;
569
570 x = Stream_GetU16_BE(data + 0);
571 y = Stream_GetU16_BE(data + 2);
572 z = Stream_GetU16_BE(data + 4);
573 data += 6;
574
575 ReadBlock(data, block)if (cpe_extBlocks) { block = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { block = *data++; }
;
576 if (World_Contains(x, y, z)) {
577 Game_UpdateBlock(x, y, z, block);
578 }
579}
580
581static void Classic_AddEntity(cc_uint8* data) {
582 static const cc_string group = String_FromConst("Players"){ "Players", (sizeof("Players") - 1), (sizeof("Players") - 1)
}
;
583 cc_string name; char nameBuffer[STRING_SIZE64];
584 cc_string skin; char skinBuffer[STRING_SIZE64];
585 EntityID id;
586 String_InitArray(name, nameBuffer)name.buffer = nameBuffer; name.length = 0; name.capacity = sizeof
(nameBuffer);
;
587 String_InitArray(skin, skinBuffer)skin.buffer = skinBuffer; skin.length = 0; skin.capacity = sizeof
(skinBuffer);
;
588
589 id = *data++;
590 ReadString(&data, &name);
591 CheckName(id, &name, &skin);
592 AddEntity(data, id, &name, &skin, true1);
593
594 /* Workaround for some servers that declare support for ExtPlayerList but don't send ExtAddPlayerName */
595 AddTablistEntry(id, &name, &name, &group, 0);
596 Classic_TabList_Set(id)(classic_tabList[id >> 3] |= (cc_uint8)(1 << (id &
0x7)))
;
597}
598
599static void Classic_EntityTeleport(cc_uint8* data) {
600 EntityID id = *data++;
601 Classic_ReadAbsoluteLocation(data, id, true1);
602}
603
604static void Classic_RelPosAndOrientationUpdate(cc_uint8* data) {
605 struct LocationUpdate update;
606 EntityID id = data[0];
607 Vec3 pos;
608 float yaw, pitch;
609
610 pos.X = (cc_int8)data[1] / 32.0f;
611 pos.Y = (cc_int8)data[2] / 32.0f;
612 pos.Z = (cc_int8)data[3] / 32.0f;
613 yaw = Math_Packed2Deg(data[4])((data[4]) * 360.0f / 256.0f);
614 pitch = Math_Packed2Deg(data[5])((data[5]) * 360.0f / 256.0f);
615
616 LocationUpdate_MakePosAndOri(&update, pos, yaw, pitch, true1);
617 UpdateLocation(id, &update, true1);
618}
619
620static void Classic_RelPositionUpdate(cc_uint8* data) {
621 struct LocationUpdate update;
622 EntityID id = data[0];
623 Vec3 pos;
624
625 pos.X = (cc_int8)data[1] / 32.0f;
626 pos.Y = (cc_int8)data[2] / 32.0f;
627 pos.Z = (cc_int8)data[3] / 32.0f;
628
629 LocationUpdate_MakePos(&update, pos, true1);
630 UpdateLocation(id, &update, true1);
631}
632
633static void Classic_OrientationUpdate(cc_uint8* data) {
634 struct LocationUpdate update;
635 EntityID id = data[0];
636 float yaw, pitch;
637
638 yaw = Math_Packed2Deg(data[1])((data[1]) * 360.0f / 256.0f);
639 pitch = Math_Packed2Deg(data[2])((data[2]) * 360.0f / 256.0f);
640
641 LocationUpdate_MakeOri(&update, yaw, pitch);
642 UpdateLocation(id, &update, true1);
643}
644
645static void Classic_RemoveEntity(cc_uint8* data) {
646 EntityID id = data[0];
647 Protocol_RemoveEntity(id);
648}
649
650static void Classic_Message(cc_uint8* data) {
651 static const cc_string detailMsg = String_FromConst("^detail.user="){ "^detail.user=", (sizeof("^detail.user=") - 1), (sizeof("^detail.user="
) - 1)}
;
652 static const cc_string detailUser = String_FromConst("^detail.user"){ "^detail.user", (sizeof("^detail.user") - 1), (sizeof("^detail.user"
) - 1)}
;
653 cc_string text; char textBuffer[STRING_SIZE64 + 2];
654
655 cc_uint8 type = *data++;
656 String_InitArray(text, textBuffer)text.buffer = textBuffer; text.length = 0; text.capacity = sizeof
(textBuffer);
;
657
658 /* Original vanilla server uses player ids for type, 255 for server messages (&e prefix) */
659 if (!cpe_useMessageTypes) {
660 if (type == 0xFF) String_AppendConst(&text, "&e");
661 type = MSG_TYPE_NORMAL;
662 }
663 ReadString(&data, &text);
664
665 /* WoM detail messages (used e.g. for fCraft server compass) */
666 if (String_CaselessStarts(&text, &detailMsg)) {
667 text = String_UNSAFE_SubstringAt(&text, detailMsg.length);
668 type = MSG_TYPE_STATUS_3;
669 }
670 /* Ignore ^detail.user.joined etc */
671 if (!String_CaselessStarts(&text, &detailUser)) Chat_AddOf(&text, type);
672}
673
674static void Classic_Kick(cc_uint8* data) {
675 static const cc_string title = String_FromConst("&eLost connection to the server"){ "&eLost connection to the server", (sizeof("&eLost connection to the server"
) - 1), (sizeof("&eLost connection to the server") - 1)}
;
676 cc_string reason = UNSAFE_GetString(data);
677 Game_Disconnect(&title, &reason);
678}
679
680static void Classic_SetPermission(cc_uint8* data) {
681 struct HacksComp* hacks = &LocalPlayer_Instance.Hacks;
682 UpdateUserType(hacks, data[0]);
683 HacksComp_RecheckFlags(hacks);
684}
685
686static void Classic_ReadAbsoluteLocation(cc_uint8* data, EntityID id, cc_bool interpolate) {
687 struct LocationUpdate update;
688 int x, y, z;
689 Vec3 pos;
690 float yaw, pitch;
691
692 if (cpe_extEntityPos) {
693 x = (int)Stream_GetU32_BE(&data[0]);
694 y = (int)Stream_GetU32_BE(&data[4]);
695 z = (int)Stream_GetU32_BE(&data[8]);
696 data += 12;
697 } else {
698 x = (cc_int16)Stream_GetU16_BE(&data[0]);
699 y = (cc_int16)Stream_GetU16_BE(&data[2]);
700 z = (cc_int16)Stream_GetU16_BE(&data[4]);
701 data += 6;
702 }
703
704 y -= 51; /* Convert to feet position */
705 if (id == ENTITIES_SELF_ID255) y += 22;
706
707 pos.X = x/32.0f; pos.Y = y/32.0f; pos.Z = z/32.0f;
708 yaw = Math_Packed2Deg(*data++)((*data++) * 360.0f / 256.0f);
709 pitch = Math_Packed2Deg(*data++)((*data++) * 360.0f / 256.0f);
710
711 if (id == ENTITIES_SELF_ID255) classic_receivedFirstPos = true1;
712 LocationUpdate_MakePosAndOri(&update, pos, yaw, pitch, false0);
713 UpdateLocation(id, &update, interpolate);
714}
715
716static void Classic_Reset(void) {
717 map_begunLoading = false0;
718 classic_receivedFirstPos = false0;
719
720 Net_Set(OPCODE_HANDSHAKE, Classic_Handshake, 131)Protocol.Handlers[OPCODE_HANDSHAKE] = Classic_Handshake; Protocol
.Sizes[OPCODE_HANDSHAKE] = 131;
;
721 Net_Set(OPCODE_PING, Classic_Ping, 1)Protocol.Handlers[OPCODE_PING] = Classic_Ping; Protocol.Sizes
[OPCODE_PING] = 1;
;
722 Net_Set(OPCODE_LEVEL_BEGIN, Classic_LevelInit, 1)Protocol.Handlers[OPCODE_LEVEL_BEGIN] = Classic_LevelInit; Protocol
.Sizes[OPCODE_LEVEL_BEGIN] = 1;
;
723 Net_Set(OPCODE_LEVEL_DATA, Classic_LevelDataChunk, 1028)Protocol.Handlers[OPCODE_LEVEL_DATA] = Classic_LevelDataChunk
; Protocol.Sizes[OPCODE_LEVEL_DATA] = 1028;
;
724 Net_Set(OPCODE_LEVEL_END, Classic_LevelFinalise, 7)Protocol.Handlers[OPCODE_LEVEL_END] = Classic_LevelFinalise; Protocol
.Sizes[OPCODE_LEVEL_END] = 7;
;
725 Net_Set(OPCODE_SET_BLOCK, Classic_SetBlock, 8)Protocol.Handlers[OPCODE_SET_BLOCK] = Classic_SetBlock; Protocol
.Sizes[OPCODE_SET_BLOCK] = 8;
;
726
727 Net_Set(OPCODE_ADD_ENTITY, Classic_AddEntity, 74)Protocol.Handlers[OPCODE_ADD_ENTITY] = Classic_AddEntity; Protocol
.Sizes[OPCODE_ADD_ENTITY] = 74;
;
728 Net_Set(OPCODE_ENTITY_TELEPORT, Classic_EntityTeleport, 10)Protocol.Handlers[OPCODE_ENTITY_TELEPORT] = Classic_EntityTeleport
; Protocol.Sizes[OPCODE_ENTITY_TELEPORT] = 10;
;
729 Net_Set(OPCODE_RELPOS_AND_ORI_UPDATE, Classic_RelPosAndOrientationUpdate, 7)Protocol.Handlers[OPCODE_RELPOS_AND_ORI_UPDATE] = Classic_RelPosAndOrientationUpdate
; Protocol.Sizes[OPCODE_RELPOS_AND_ORI_UPDATE] = 7;
;
730 Net_Set(OPCODE_RELPOS_UPDATE, Classic_RelPositionUpdate, 5)Protocol.Handlers[OPCODE_RELPOS_UPDATE] = Classic_RelPositionUpdate
; Protocol.Sizes[OPCODE_RELPOS_UPDATE] = 5;
;
731 Net_Set(OPCODE_ORI_UPDATE, Classic_OrientationUpdate, 4)Protocol.Handlers[OPCODE_ORI_UPDATE] = Classic_OrientationUpdate
; Protocol.Sizes[OPCODE_ORI_UPDATE] = 4;
;
732 Net_Set(OPCODE_REMOVE_ENTITY, Classic_RemoveEntity, 2)Protocol.Handlers[OPCODE_REMOVE_ENTITY] = Classic_RemoveEntity
; Protocol.Sizes[OPCODE_REMOVE_ENTITY] = 2;
;
733
734 Net_Set(OPCODE_MESSAGE, Classic_Message, 66)Protocol.Handlers[OPCODE_MESSAGE] = Classic_Message; Protocol
.Sizes[OPCODE_MESSAGE] = 66;
;
735 Net_Set(OPCODE_KICK, Classic_Kick, 65)Protocol.Handlers[OPCODE_KICK] = Classic_Kick; Protocol.Sizes
[OPCODE_KICK] = 65;
;
736 Net_Set(OPCODE_SET_PERMISSION, Classic_SetPermission, 2)Protocol.Handlers[OPCODE_SET_PERMISSION] = Classic_SetPermission
; Protocol.Sizes[OPCODE_SET_PERMISSION] = 2;
;
737}
738
739static void Classic_Tick(void) {
740 struct Entity* p = &LocalPlayer_Instance.Base;
741 if (!classic_receivedFirstPos) return;
742 Classic_WritePosition(p->Position, p->Yaw, p->Pitch);
743}
744
745
746/*########################################################################################################################*
747*------------------------------------------------------CPE protocol-------------------------------------------------------*
748*#########################################################################################################################*/
749static const char* cpe_clientExtensions[35] = {
750 "ClickDistance", "CustomBlocks", "HeldBlock", "EmoteFix", "TextHotKey", "ExtPlayerList",
751 "EnvColors", "SelectionCuboid", "BlockPermissions", "ChangeModel", "EnvMapAppearance",
752 "EnvWeatherType", "MessageTypes", "HackControl", "PlayerClick", "FullCP437", "LongerMessages",
753 "BlockDefinitions", "BlockDefinitionsExt", "BulkBlockUpdate", "TextColors", "EnvMapAspect",
754 "EntityProperty", "ExtEntityPositions", "TwoWayPing", "InventoryOrder", "InstantMOTD", "FastMap", "SetHotbar",
755 "SetSpawnpoint", "VelocityControl", "CustomParticles", "CustomModels",
756 /* NOTE: These must be placed last for when EXTENDED_TEXTURES or EXTENDED_BLOCKS are not defined */
757 "ExtendedTextures", "ExtendedBlocks"
758};
759static void CPE_SetMapEnvUrl(cc_uint8* data);
760
761#define Ext_Deg2Packed(x)((int)((x) * 65536.0f / 360.0f)) ((int)((x) * 65536.0f / 360.0f))
762void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct RayTracer* t) {
763 struct Entity* p = &LocalPlayer_Instance.Base;
764 cc_uint8 data[15];
765
766 data[0] = OPCODE_PLAYER_CLICK;
767 {
768 data[1] = button;
769 data[2] = !pressed;
770 Stream_SetU16_BE(&data[3], Ext_Deg2Packed(p->Yaw)((int)((p->Yaw) * 65536.0f / 360.0f)));
771 Stream_SetU16_BE(&data[5], Ext_Deg2Packed(p->Pitch)((int)((p->Pitch) * 65536.0f / 360.0f)));
772
773 data[7] = targetId;
774 Stream_SetU16_BE(&data[8], t->pos.X);
775 Stream_SetU16_BE(&data[10], t->pos.Y);
776 Stream_SetU16_BE(&data[12], t->pos.Z);
777
778 data[14] = 255;
779 /* Our own face values differ from CPE block face */
780 switch (t->Closest) {
781 case FACE_XMAX: data[14] = 0; break;
782 case FACE_XMIN: data[14] = 1; break;
783 case FACE_YMAX: data[14] = 2; break;
784 case FACE_YMIN: data[14] = 3; break;
785 case FACE_ZMAX: data[14] = 4; break;
786 case FACE_ZMIN: data[14] = 5; break;
787 }
788 }
789 Server.SendData(data, 15);
790}
791
792static void CPE_SendExtInfo(int extsCount) {
793 cc_uint8 data[67];
794 data[0] = OPCODE_EXT_INFO;
795 {
796 WriteString(data + 1, &Server.AppName);
797 Stream_SetU16_BE(data + 65, extsCount);
798 }
799 Server.SendData(data, 67);
800}
801
802static void CPE_SendExtEntry(const cc_string* extName, int extVersion) {
803 cc_uint8 data[69];
804 data[0] = OPCODE_EXT_ENTRY;
805 {
806 WriteString(data + 1, extName);
807 Stream_SetU32_BE(data + 65, extVersion);
808 }
809 Server.SendData(data, 69);
810}
811
812static void CPE_WriteTwoWayPing(cc_bool serverToClient, int id) {
813 cc_uint8* data = Server.WriteBuffer;
814 *data++ = OPCODE_TWO_WAY_PING;
815 {
816 *data++ = serverToClient;
817 Stream_SetU16_BE(data, id); data += 2;
818 }
819 Server.WriteBuffer = data;
820}
821
822static void CPE_SendCpeExtInfoReply(void) {
823 int count = Array_Elems(cpe_clientExtensions)(sizeof(cpe_clientExtensions) / sizeof(cpe_clientExtensions[0
]))
;
824 cc_string name;
825 int i, ver;
826
827 if (cpe_serverExtensionsCount) return;
828
829#ifndef EXTENDED_TEXTURES
830 count--;
831#endif
832#ifndef EXTENDED_BLOCKS
833 count--;
834#endif
835
836#ifdef EXTENDED_BLOCKS
837 if (!Game_AllowCustomBlocks) count -= 3;
838#else
839 if (!Game_AllowCustomBlocks) count -= 2;
840#endif
841 CPE_SendExtInfo(count);
842
843 for (i = 0; i < Array_Elems(cpe_clientExtensions)(sizeof(cpe_clientExtensions) / sizeof(cpe_clientExtensions[0
]))
; i++) {
844 name = String_FromReadonly(cpe_clientExtensions[i]);
845 ver = 1;
846
847 if (String_CaselessEqualsConst(&name, "ExtPlayerList")) ver = 2;
848 if (String_CaselessEqualsConst(&name, "EnvMapAppearance")) ver = cpe_envMapVer;
849 if (String_CaselessEqualsConst(&name, "BlockDefinitionsExt")) ver = cpe_blockDefsExtVer;
850 if (String_CaselessEqualsConst(&name, "CustomModels")) ver = cpe_customModelsVer;
851
852 if (!Game_AllowCustomBlocks) {
853 if (String_CaselessEqualsConst(&name, "BlockDefinitionsExt")) continue;
854 if (String_CaselessEqualsConst(&name, "BlockDefinitions")) continue;
855#ifdef EXTENDED_BLOCKS
856 if (String_CaselessEqualsConst(&name, "ExtendedBlocks")) continue;
857#endif
858 }
859
860#ifndef EXTENDED_TEXTURES
861 if (String_CaselessEqualsConst(&name, "ExtendedTextures")) continue;
862#endif
863#ifndef EXTENDED_BLOCKS
864 if (String_CaselessEqualsConst(&name, "ExtendedBlocks")) continue;
865#endif
866 CPE_SendExtEntry(&name, ver);
867 }
868}
869
870static void CPE_ExtInfo(cc_uint8* data) {
871 static const cc_string d3Server = String_FromConst("D3 server"){ "D3 server", (sizeof("D3 server") - 1), (sizeof("D3 server"
) - 1)}
;
872 cc_string appName = UNSAFE_GetString(data);
873 cpe_needD3Fix = String_CaselessStarts(&appName, &d3Server);
874 Chat_Add1("Server software: %s", &appName);
875
876 /* Workaround for old MCGalaxy that send ExtEntry sync but ExtInfo async. */
877 /* Means ExtEntry may sometimes arrive before ExtInfo, so use += instead of = */
878 cpe_serverExtensionsCount += Stream_GetU16_BE(&data[64]);
879 CPE_SendCpeExtInfoReply();
880}
881
882static void CPE_ExtEntry(cc_uint8* data) {
883 cc_string ext = UNSAFE_GetString(data);
884 int version = data[67];
885 Platform_Log2("cpe ext: %s, %i", &ext, &version);
886
887 cpe_serverExtensionsCount--;
888 CPE_SendCpeExtInfoReply();
889
890 /* update support state */
891 if (String_CaselessEqualsConst(&ext, "HeldBlock")) {
892 cpe_sendHeldBlock = true1;
893 } else if (String_CaselessEqualsConst(&ext, "MessageTypes")) {
894 cpe_useMessageTypes = true1;
895 } else if (String_CaselessEqualsConst(&ext, "ExtPlayerList")) {
896 Server.SupportsExtPlayerList = true1;
897 } else if (String_CaselessEqualsConst(&ext, "BlockPermissions")) {
898 cpe_blockPerms = true1;
899 } else if (String_CaselessEqualsConst(&ext, "PlayerClick")) {
900 Server.SupportsPlayerClick = true1;
901 } else if (String_CaselessEqualsConst(&ext, "EnvMapAppearance")) {
902 cpe_envMapVer = version;
903 if (version == 1) return;
904 Protocol.Sizes[OPCODE_ENV_SET_MAP_APPEARANCE] += 4;
905 } else if (String_CaselessEqualsConst(&ext, "LongerMessages")) {
906 Server.SupportsPartialMessages = true1;
907 } else if (String_CaselessEqualsConst(&ext, "FullCP437")) {
908 Server.SupportsFullCP437 = true1;
909 } else if (String_CaselessEqualsConst(&ext, "BlockDefinitionsExt")) {
910 cpe_blockDefsExtVer = version;
911 if (version == 1) return;
912 Protocol.Sizes[OPCODE_DEFINE_BLOCK_EXT] += 3;
913 } else if (String_CaselessEqualsConst(&ext, "ExtEntityPositions")) {
914 Protocol.Sizes[OPCODE_ENTITY_TELEPORT] += 6;
915 Protocol.Sizes[OPCODE_ADD_ENTITY] += 6;
916 Protocol.Sizes[OPCODE_EXT_ADD_ENTITY2] += 6;
917 Protocol.Sizes[OPCODE_SET_SPAWNPOINT] += 6;
918 cpe_extEntityPos = true1;
919 } else if (String_CaselessEqualsConst(&ext, "TwoWayPing")) {
920 cpe_twoWayPing = true1;
921 } else if (String_CaselessEqualsConst(&ext, "FastMap")) {
922 Protocol.Sizes[OPCODE_LEVEL_BEGIN] += 4;
923 cpe_fastMap = true1;
924 } else if (String_CaselessEqualsConst(&ext, "CustomModels")) {
925 cpe_customModelsVer = min(2, version)((2) < (version) ? (2) : (version));
926 if (version == 2) {
927 Protocol.Sizes[OPCODE_DEFINE_MODEL_PART] = 167;
928 }
929 }
930#ifdef EXTENDED_TEXTURES
931 else if (String_CaselessEqualsConst(&ext, "ExtendedTextures")) {
932 Protocol.Sizes[OPCODE_DEFINE_BLOCK] += 3;
933 Protocol.Sizes[OPCODE_DEFINE_BLOCK_EXT] += 6;
934 cpe_extTextures = true1;
935 }
936#endif
937#ifdef EXTENDED_BLOCKS
938 else if (String_CaselessEqualsConst(&ext, "ExtendedBlocks")) {
939 if (!Game_AllowCustomBlocks) return;
940 cpe_extBlocks = true1;
941
942 Protocol.Sizes[OPCODE_SET_BLOCK] += 1;
943 Protocol.Sizes[OPCODE_HOLD_THIS] += 1;
944 Protocol.Sizes[OPCODE_SET_BLOCK_PERMISSION] += 1;
945 Protocol.Sizes[OPCODE_DEFINE_BLOCK] += 1;
946 Protocol.Sizes[OPCODE_UNDEFINE_BLOCK] += 1;
947 Protocol.Sizes[OPCODE_DEFINE_BLOCK_EXT] += 1;
948 Protocol.Sizes[OPCODE_SET_INVENTORY_ORDER] += 2;
949 Protocol.Sizes[OPCODE_BULK_BLOCK_UPDATE] += 256 / 4;
950 Protocol.Sizes[OPCODE_SET_HOTBAR] += 1;
951 }
952#endif
953}
954
955static void CPE_SetClickDistance(cc_uint8* data) {
956 LocalPlayer_Instance.ReachDistance = Stream_GetU16_BE(data) / 32.0f;
957}
958
959static void CPE_CustomBlockLevel(cc_uint8* data) {
960 /* reply with version 1 level support */
961 cc_uint8 reply[2] = { OPCODE_CUSTOM_BLOCK_LEVEL, 1 };
962 Server.SendData(reply, 2);
963
964 Game_UseCPEBlocks = true1;
965 Event_RaiseVoid(&BlockEvents.PermissionsChanged);
966}
967
968static void CPE_HoldThis(cc_uint8* data) {
969 BlockID block;
970 cc_bool canChange;
971
972 ReadBlock(data, block)if (cpe_extBlocks) { block = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { block = *data++; }
;
973 canChange = *data == 0;
974
975 Inventory.CanChangeSelected = true1;
976 Inventory_SetSelectedBlock(block);
977 Inventory.CanChangeSelected = canChange;
978}
979
980static void CPE_SetTextHotkey(cc_uint8* data) {
981 /* First 64 bytes are label string */
982 cc_string action = UNSAFE_GetString(&data[64]);
983 cc_uint32 keyCode = Stream_GetU32_BE(&data[128]);
984 cc_uint8 keyMods = data[132];
985 int key;
986
987 if (keyCode > 255) return;
988 key = Hotkeys_LWJGL[keyCode];
989 if (!key) return;
990 Platform_Log3("CPE hotkey added: %c, %b: %s", Input_Names[key], &keyMods, &action);
991
992 if (!action.length) {
993 Hotkeys_Remove(key, keyMods);
994 StoredHotkeys_Load(key, keyMods);
995 } else if (action.buffer[action.length - 1] == '\n') {
996 action.length--;
997 Hotkeys_Add(key, keyMods, &action, false0);
998 } else { /* more input needed by user */
999 Hotkeys_Add(key, keyMods, &action, true1);
1000 }
1001}
1002
1003static void CPE_ExtAddPlayerName(cc_uint8* data) {
1004 EntityID id = data[1]; /* 16 bit id */
1005 cc_string playerName = UNSAFE_GetString(&data[2]);
1006 cc_string listName = UNSAFE_GetString(&data[66]);
1007 cc_string groupName = UNSAFE_GetString(&data[130]);
1008 cc_uint8 groupRank = data[194];
1009
1010 RemoveEndPlus(&playerName);
1011 RemoveEndPlus(&listName);
1012
1013 /* Workarond for server software that declares support for ExtPlayerList, but sends AddEntity then AddPlayerName */
1014 Classic_TabList_Reset(id)(classic_tabList[id >> 3] &= (cc_uint8)~(1 <<
(id & 0x7)))
;
1015 AddTablistEntry(id, &playerName, &listName, &groupName, groupRank);
1016}
1017
1018static void CPE_ExtAddEntity(cc_uint8* data) {
1019 cc_string name, skin;
1020 EntityID id;
1021
1022 id = data[0];
1023 name = UNSAFE_GetString(data + 1);
1024 skin = UNSAFE_GetString(data + 65);
1025
1026 CheckName(id, &name, &skin);
1027 AddEntity(data + 129, id, &name, &skin, false0);
1028}
1029
1030static void CPE_ExtRemovePlayerName(cc_uint8* data) {
1031 EntityID id = data[1];
1032 TabList_Remove(id);
1033}
1034
1035static void CPE_MakeSelection(cc_uint8* data) {
1036 IVec3 p1, p2;
1037 PackedCol c;
1038 /* data[0] is id, data[1..64] is label */
1039
1040 p1.X = (cc_int16)Stream_GetU16_BE(data + 65);
1041 p1.Y = (cc_int16)Stream_GetU16_BE(data + 67);
1042 p1.Z = (cc_int16)Stream_GetU16_BE(data + 69);
1043 p2.X = (cc_int16)Stream_GetU16_BE(data + 71);
1044 p2.Y = (cc_int16)Stream_GetU16_BE(data + 73);
1045 p2.Z = (cc_int16)Stream_GetU16_BE(data + 75);
1046
1047 /* R,G,B,A are actually 16 bit unsigned integers */
1048 c = PackedCol_Make(data[78], data[80], data[82], data[84])(((cc_uint8)(data[78]) << 0) | ((cc_uint8)(data[80]) <<
8) | ((cc_uint8)(data[82]) << 16) | ((cc_uint8)(data[84
]) << 24))
;
1049 Selections_Add(data[0], &p1, &p2, c);
1050}
1051
1052static void CPE_RemoveSelection(cc_uint8* data) {
1053 Selections_Remove(data[0]);
1054}
1055
1056static void CPE_SetEnvCol(cc_uint8* data) {
1057 PackedCol c;
1058 cc_uint8 variable;
1059 cc_bool invalid;
1060
1061 variable = data[0];
1062 invalid = data[1] || data[3] || data[5];
1063 /* R,G,B are actually 16 bit unsigned integers */
1064 /* Above > 255 is 'invalid' (this is used by servers) */
1065 c = PackedCol_Make(data[2], data[4], data[6], 255)(((cc_uint8)(data[2]) << 0) | ((cc_uint8)(data[4]) <<
8) | ((cc_uint8)(data[6]) << 16) | ((cc_uint8)(255) <<
24))
;
1066
1067 if (variable == 0) {
1068 Env_SetSkyCol(invalid ? ENV_DEFAULT_SKY_COL(((cc_uint8)(0x99) << 0) | ((cc_uint8)(0xCC) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
: c);
1069 } else if (variable == 1) {
1070 Env_SetCloudsCol(invalid ? ENV_DEFAULT_CLOUDS_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
: c);
1071 } else if (variable == 2) {
1072 Env_SetFogCol(invalid ? ENV_DEFAULT_FOG_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
: c);
1073 } else if (variable == 3) {
1074 Env_SetShadowCol(invalid ? ENV_DEFAULT_SHADOW_COL(((cc_uint8)(0x9B) << 0) | ((cc_uint8)(0x9B) << 8
) | ((cc_uint8)(0x9B) << 16) | ((cc_uint8)(0xFF) <<
24))
: c);
1075 } else if (variable == 4) {
1076 Env_SetSunCol(invalid ? ENV_DEFAULT_SUN_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
: c);
1077 } else if (variable == 5) {
1078 Env_SetSkyboxCol(invalid ? ENV_DEFAULT_SKYBOX_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
: c);
1079 }
1080}
1081
1082static void CPE_SetBlockPermission(cc_uint8* data) {
1083 BlockID block;
1084 ReadBlock(data, block)if (cpe_extBlocks) { block = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { block = *data++; }
;
1085
1086 Blocks.CanPlace[block] = *data++ != 0;
1087 Blocks.CanDelete[block] = *data++ != 0;
1088 Event_RaiseVoid(&BlockEvents.PermissionsChanged);
1089}
1090
1091static void CPE_ChangeModel(cc_uint8* data) {
1092 struct Entity* e;
1093 EntityID id = data[0];
1094 cc_string model = UNSAFE_GetString(data + 1);
1095
1096 e = Entities.List[id];
1097 if (e) Entity_SetModel(e, &model);
1098}
1099
1100static void CPE_EnvSetMapAppearance(cc_uint8* data) {
1101 int maxViewDist;
1102
1103 CPE_SetMapEnvUrl(data);
1104 Env_SetSidesBlock(data[64]);
1105 Env_SetEdgeBlock(data[65]);
1106 Env_SetEdgeHeight((cc_int16)Stream_GetU16_BE(data + 66));
1107 if (cpe_envMapVer == 1) return;
1108
1109 /* Version 2 */
1110 Env_SetCloudsHeight((cc_int16)Stream_GetU16_BE(data + 68));
1111 maxViewDist = (cc_int16)Stream_GetU16_BE(data + 70);
1112 Game_MaxViewDistance = maxViewDist <= 0 ? DEFAULT_MAX_VIEWDIST32768 : maxViewDist;
1113 Game_SetViewDistance(Game_UserViewDistance);
1114}
1115
1116static void CPE_EnvWeatherType(cc_uint8* data) {
1117 Env_SetWeather(data[0]);
1118}
1119
1120static void CPE_HackControl(cc_uint8* data) {
1121 struct LocalPlayer* p = &LocalPlayer_Instance;
1122 int jumpHeight;
1123
1124 p->Hacks.CanFly = data[0] != 0;
1125 p->Hacks.CanNoclip = data[1] != 0;
1126 p->Hacks.CanSpeed = data[2] != 0;
1127 p->Hacks.CanRespawn = data[3] != 0;
1128 p->Hacks.CanUseThirdPerson = data[4] != 0;
1129 HacksComp_Update(&p->Hacks);
1130 jumpHeight = Stream_GetU16_BE(data + 5);
1131
1132 if (jumpHeight == UInt16_MaxValue((cc_uint16)65535)) { /* special value of -1 to reset default */
1133 LocalPlayer_ResetJumpVelocity();
1134 } else {
1135 p->Physics.JumpVel = PhysicsComp_CalcJumpVelocity(jumpHeight / 32.0f);
1136 p->Physics.ServerJumpVel = p->Physics.JumpVel;
1137 }
1138}
1139
1140static void CPE_ExtAddEntity2(cc_uint8* data) {
1141 cc_string name, skin;
1142 EntityID id;
1143
1144 id = data[0];
1145 name = UNSAFE_GetString(data + 1);
1146 skin = UNSAFE_GetString(data + 65);
1147
1148 CheckName(id, &name, &skin);
1149 AddEntity(data + 129, id, &name, &skin, true1);
1150}
1151
1152#define BULK_MAX_BLOCKS256 256
1153static void CPE_BulkBlockUpdate(cc_uint8* data) {
1154 cc_int32 indices[BULK_MAX_BLOCKS256];
1155 BlockID blocks[BULK_MAX_BLOCKS256];
1156 int index, i;
1157 int x, y, z;
1158 int count = 1 + *data++;
1159
1160 for (i = 0; i
0.1
'i' is < 'count'
< count
; i++) {
1
Loop condition is true. Entering loop body
2
Assuming 'i' is < 'count'
3
Loop condition is true. Entering loop body
4
Assuming 'i' is < 'count'
5
Loop condition is true. Entering loop body
6
Assuming 'i' is >= 'count'
7
Loop condition is false. Execution continues on line 1163
1161 indices[i] = Stream_GetU32_BE(data); data += 4;
1162 }
1163 data += (BULK_MAX_BLOCKS256 - count) * 4;
1164
1165 for (i = 0; i < count; i++) {
8
Loop condition is true. Entering loop body
9
Loop condition is true. Entering loop body
10
Loop condition is true. Entering loop body
11
Loop condition is false. Execution continues on line 1168
1166 blocks[i] = data[i];
1167 }
1168 data += BULK_MAX_BLOCKS256;
1169
1170 if (cpe_extBlocks) {
12
Assuming 'cpe_extBlocks' is not equal to 0
13
Taking true branch
1171 for (i = 0; i < count; i += 4) {
14
Loop condition is true. Entering loop body
1172 cc_uint8 flags = data[i >> 2];
1173 blocks[i + 0] |= (BlockID)((flags & 0x03) << 8);
1174 blocks[i + 1] |= (BlockID)((flags & 0x0C) << 6);
1175 blocks[i + 2] |= (BlockID)((flags & 0x30) << 4);
1176 blocks[i + 3] |= (BlockID)((flags & 0xC0) << 2);
15
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage
1177 }
1178 data += BULK_MAX_BLOCKS256 / 4;
1179 }
1180
1181 for (i = 0; i < count; i++) {
1182 index = indices[i];
1183 if (index < 0 || index >= World.Volume) continue;
1184 World_Unpack(index, x, y, z)x = index % World.Width; z = (index / World.Width) % World.Length
; y = (index / World.Width) / World.Length;
;
1185
1186#ifdef EXTENDED_BLOCKS
1187 Game_UpdateBlock(x, y, z, blocks[i] % BLOCK_COUNT);
1188#else
1189 Game_UpdateBlock(x, y, z, blocks[i]);
1190#endif
1191 }
1192}
1193
1194static void CPE_SetTextColor(cc_uint8* data) {
1195 BitmapCol c = BitmapCol_Make(data[0], data[1], data[2], data[3])(((cc_uint8)(data[0]) << 16) | ((cc_uint8)(data[1]) <<
8) | ((cc_uint8)(data[2]) << 0) | ((cc_uint8)(data[3])
<< 24))
;
1196 cc_uint8 code = data[4];
1197
1198 /* disallow space, null, and colour code specifiers */
1199 if (code == '\0' || code == ' ' || code == 0xFF) return;
1200 if (code == '%' || code == '&') return;
1201
1202 Drawer2D_Cols[code] = c;
1203 Event_RaiseInt(&ChatEvents.ColCodeChanged, code);
1204}
1205
1206static void CPE_SetMapEnvUrl(cc_uint8* data) {
1207 cc_string url = UNSAFE_GetString(data);
1208
1209 if (!url.length || Utils_IsUrlPrefix(&url)) {
1210 Server_RetrieveTexturePack(&url);
1211 }
1212 Platform_Log1("Tex url: %s", &url);
1213}
1214
1215static void CPE_SetMapEnvProperty(cc_uint8* data) {
1216 int value = (int)Stream_GetU32_BE(data + 1);
1217 Math_Clamp(value, -0xFFFFFF, 0xFFFFFF)value = value < (-0xFFFFFF) ? (-0xFFFFFF) : value; value =
value > (0xFFFFFF) ? (0xFFFFFF) : value;
;
1218
1219 switch (data[0]) {
1220 case 0:
1221 Math_Clamp(value, 0, BLOCK_MAX_DEFINED)value = value < (0) ? (0) : value; value = value > (BLOCK_MAX_DEFINED
) ? (BLOCK_MAX_DEFINED) : value;
;
1222 Env_SetSidesBlock((BlockID)value); break;
1223 case 1:
1224 Math_Clamp(value, 0, BLOCK_MAX_DEFINED)value = value < (0) ? (0) : value; value = value > (BLOCK_MAX_DEFINED
) ? (BLOCK_MAX_DEFINED) : value;
;
1225 Env_SetEdgeBlock((BlockID)value); break;
1226 case 2:
1227 Env_SetEdgeHeight(value); break;
1228 case 3:
1229 Env_SetCloudsHeight(value); break;
1230 case 4:
1231 Math_Clamp(value, -0x7FFF, 0x7FFF)value = value < (-0x7FFF) ? (-0x7FFF) : value; value = value
> (0x7FFF) ? (0x7FFF) : value;
;
1232 Game_MaxViewDistance = value <= 0 ? DEFAULT_MAX_VIEWDIST32768 : value;
1233 Game_SetViewDistance(Game_UserViewDistance); break;
1234 case 5:
1235 Env_SetCloudsSpeed(value / 256.0f); break;
1236 case 6:
1237 Env_SetWeatherSpeed(value / 256.0f); break;
1238 case 7:
1239 Env_SetWeatherFade(value / 128.0f); break;
1240 case 8:
1241 Env_SetExpFog(value != 0); break;
1242 case 9:
1243 Env_SetSidesOffset(value); break;
1244 case 10:
1245 Env_SetSkyboxHorSpeed(value / 1024.0f); break;
1246 case 11:
1247 Env_SetSkyboxVerSpeed(value / 1024.0f); break;
1248 }
1249}
1250
1251static void CPE_SetEntityProperty(cc_uint8* data) {
1252 struct LocationUpdate update = { 0 };
1253 struct Entity* e;
1254 float scale;
1255
1256 EntityID id = data[0];
1257 cc_uint8 type = data[1];
1258 int value = (int)Stream_GetU32_BE(data + 2);
1259
1260 e = Entities.List[id];
1261 if (!e) return;
1262
1263 switch (type) {
1264 case 0:
1265 update.Flags = LOCATIONUPDATE_ROTX0x08;
1266 update.RotX = LocationUpdate_Clamp((float)value); break;
1267 case 1:
1268 update.Flags = LOCATIONUPDATE_YAW0x04;
1269 update.Yaw = LocationUpdate_Clamp((float)value); break;
1270 case 2:
1271 update.Flags = LOCATIONUPDATE_ROTZ0x10;
1272 update.RotZ = LocationUpdate_Clamp((float)value); break;
1273
1274 case 3:
1275 case 4:
1276 case 5:
1277 scale = value / 1000.0f;
1278 if (e->ModelRestrictedScale) {
1279 Math_Clamp(scale, 0.01f, e->Model->maxScale)scale = scale < (0.01f) ? (0.01f) : scale; scale = scale >
(e->Model->maxScale) ? (e->Model->maxScale) : scale
;
;
1280 }
1281
1282 if (type == 3) e->ModelScale.X = scale;
1283 if (type == 4) e->ModelScale.Y = scale;
1284 if (type == 5) e->ModelScale.Z = scale;
1285
1286 Entity_UpdateModelBounds(e);
1287 return;
1288 default:
1289 return;
1290 }
1291 e->VTABLE->SetLocation(e, &update, true1);
1292}
1293
1294static void CPE_TwoWayPing(cc_uint8* data) {
1295 cc_uint8 serverToClient = data[0];
1296 int id = Stream_GetU16_BE(data + 1);
1297
1298 if (serverToClient) {
1299 CPE_WriteTwoWayPing(true1, id); /* server to client reply */
1300 Net_SendPacket();
1301 } else { Ping_Update(id); }
1302}
1303
1304static void CPE_SetInventoryOrder(cc_uint8* data) {
1305 BlockID block, order;
1306 ReadBlock(data, block)if (cpe_extBlocks) { block = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { block = *data++; }
;
1307 ReadBlock(data, order)if (cpe_extBlocks) { order = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { order = *data++; }
;
1308
1309 Inventory_Remove(block);
1310 if (order) { Inventory.Map[order - 1] = block; }
1311}
1312
1313static void CPE_SetHotbar(cc_uint8* data) {
1314 BlockID block;
1315 cc_uint8 index;
1316 ReadBlock(data, block)if (cpe_extBlocks) { block = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { block = *data++; }
;
1317 index = *data;
1318
1319 if (index >= INVENTORY_BLOCKS_PER_HOTBAR9) return;
1320 Inventory_Set(index, block)Inventory.Table[Inventory.Offset + (index)] = block;
1321}
1322
1323static void CPE_SetSpawnPoint(cc_uint8* data) {
1324 struct LocalPlayer* p = &LocalPlayer_Instance;
1325 int x, y, z;
1326
1327 if (cpe_extEntityPos) {
1328 x = (int)Stream_GetU32_BE(&data[0]);
1329 y = (int)Stream_GetU32_BE(&data[4]);
1330 z = (int)Stream_GetU32_BE(&data[8]);
1331 data += 12;
1332 } else {
1333 x = (cc_int16)Stream_GetU16_BE(&data[0]);
1334 y = (cc_int16)Stream_GetU16_BE(&data[2]);
1335 z = (cc_int16)Stream_GetU16_BE(&data[4]);
1336 data += 6;
1337 }
1338 p->SpawnYaw = Math_Packed2Deg(*data++)((*data++) * 360.0f / 256.0f);
1339 p->SpawnPitch = Math_Packed2Deg(*data++)((*data++) * 360.0f / 256.0f);
1340
1341 y -= 51; /* Convert to feet position */
1342 Vec3_Set(p->Spawn, (float)(x / 32.0f), (float)(y / 32.0f), (float)(z / 32.0f))(p->Spawn).X = (float)(x / 32.0f); (p->Spawn).Y = (float
)(y / 32.0f); (p->Spawn).Z = (float)(z / 32.0f);
;
1343}
1344
1345static void CalcVelocity(float* vel, cc_uint8* src, cc_uint8 mode) {
1346 int raw = (int)Stream_GetU32_BE(src);
1347 float value = Math_AbsF(raw / 10000.0f)__builtin_fabsf(raw / 10000.0f);
1348 value = Math_Sign(raw) * PhysicsComp_CalcJumpVelocity(value);
1349
1350 if (mode == 0) *vel += value;
1351 if (mode == 1) *vel = value;
1352}
1353
1354static void CPE_VelocityControl(cc_uint8* data) {
1355 struct LocalPlayer* p = &LocalPlayer_Instance;
1356 CalcVelocity(&p->Base.Velocity.X, data + 0, data[12]);
1357 CalcVelocity(&p->Base.Velocity.Y, data + 4, data[13]);
1358 CalcVelocity(&p->Base.Velocity.Z, data + 8, data[14]);
1359}
1360
1361static void CPE_DefineEffect(cc_uint8* data) {
1362 struct CustomParticleEffect* e = &Particles_CustomEffects[data[0]];
1363
1364 /* e.g. bounds of 0,0, 15,15 gives an 8x8 icon in the default 128x128 particles.png */
1365 e->rec.U1 = data[1] / 256.0f;
1366 e->rec.V1 = data[2] / 256.0f;
1367 e->rec.U2 = (data[3] + 1) / 256.0f;
1368 e->rec.V2 = (data[4] + 1) / 256.0f;
1369
1370 e->tintCol = PackedCol_Make(data[5], data[6], data[7], 255)(((cc_uint8)(data[5]) << 0) | ((cc_uint8)(data[6]) <<
8) | ((cc_uint8)(data[7]) << 16) | ((cc_uint8)(255) <<
24))
;
1371 e->frameCount = data[8];
1372 e->particleCount = data[9];
1373 e->size = data[10] / 32.0f; /* 32 units per block */
1374
1375 e->sizeVariation = (int)Stream_GetU32_BE(data + 11) / 10000.0f;
1376 e->spread = Stream_GetU16_BE(data + 15) / 32.0f;
1377 e->speed = (int)Stream_GetU32_BE(data + 17) / 10000.0f;
1378 e->gravity = (int)Stream_GetU32_BE(data + 21) / 10000.0f;
1379 e->baseLifetime = (int)Stream_GetU32_BE(data + 25) / 10000.0f;
1380 e->lifetimeVariation = (int)Stream_GetU32_BE(data + 29) / 10000.0f;
1381
1382 e->collideFlags = data[33];
1383 e->fullBright = data[34];
1384}
1385
1386static void CPE_SpawnEffect(cc_uint8* data) {
1387 float x, y, z, originX, originY, originZ;
1388
1389 x = (int)Stream_GetU32_BE(data + 1) / 32.0f;
1390 y = (int)Stream_GetU32_BE(data + 5) / 32.0f;
1391 z = (int)Stream_GetU32_BE(data + 9) / 32.0f;
1392 originX = (int)Stream_GetU32_BE(data + 13) / 32.0f;
1393 originY = (int)Stream_GetU32_BE(data + 17) / 32.0f;
1394 originZ = (int)Stream_GetU32_BE(data + 21) / 32.0f;
1395
1396 Particles_CustomEffect(data[0], x, y, z, originX, originY, originZ);
1397}
1398
1399static void CPE_DefineModel(cc_uint8* data) {
1400 cc_uint8 id = data[0];
1401 struct CustomModel* cm = &custom_models[id];
1402 cc_string name;
1403 cc_uint8 flags;
1404 cc_uint8 numParts;
1405
1406 if (id >= MAX_CUSTOM_MODELS64) return;
1407 CustomModel_Undefine(cm);
1408 Model_Init(&cm->model);
1409
1410 name = UNSAFE_GetString(data + 1);
1411 String_CopyToRawArray(cm->name, &name)String_CopyToRaw(cm->name, sizeof(cm->name), &name);
1412
1413 flags = data[65];
1414 cm->model.bobbing = flags & 0x01;
1415 cm->model.pushes = flags & 0x02;
1416 cm->model.usesHumanSkin = flags & 0x04;
1417 cm->model.calcHumanAnims = flags & 0x08;
1418
1419 cm->nameY = GetFloat(data + 66);
1420 cm->eyeY = GetFloat(data + 70);
1421
1422 cm->collisionBounds.X = GetFloat(data + 74);
1423 cm->collisionBounds.Y = GetFloat(data + 78);
1424 cm->collisionBounds.Z = GetFloat(data + 82);
1425
1426 cm->pickingBoundsAABB.Min.X = GetFloat(data + 86);
1427 cm->pickingBoundsAABB.Min.Y = GetFloat(data + 90);
1428 cm->pickingBoundsAABB.Min.Z = GetFloat(data + 94);
1429
1430 cm->pickingBoundsAABB.Max.X = GetFloat(data + 98);
1431 cm->pickingBoundsAABB.Max.Y = GetFloat(data + 102);
1432 cm->pickingBoundsAABB.Max.Z = GetFloat(data + 106);
1433
1434 cm->uScale = Stream_GetU16_BE(data + 110);
1435 cm->vScale = Stream_GetU16_BE(data + 112);
1436 numParts = data[114];
1437
1438 if (numParts > MAX_CUSTOM_MODEL_PARTS64) {
1439 cc_string msg; char msgBuffer[256];
1440 String_InitArray(msg, msgBuffer)msg.buffer = msgBuffer; msg.length = 0; msg.capacity = sizeof
(msgBuffer);
;
1441
1442 String_Format1(
1443 &msg,
1444 "&cCustom Model '%s' exceeds parts limit of " STRINGIFY(MAX_CUSTOM_MODEL_PARTS)"64",
1445 &name
1446 );
1447 Logger_WarnFunc(&msg);
1448 return;
1449 }
1450
1451 cm->numParts = numParts;
1452 cm->model.vertices = (struct ModelVertex*)Mem_AllocCleared(numParts * MODEL_BOX_VERTICES(FACE_COUNT * 4),
1453 sizeof(struct ModelVertex), "CustomModel vertices");
1454 cm->defined = true1;
1455}
1456
1457static void CPE_DefineModelPart(cc_uint8* data) {
1458 cc_uint8 id = data[0];
1459 struct CustomModel* m = &custom_models[id];
1460 struct CustomModelPart* part;
1461 struct CustomModelPartDef p;
1462 int i;
1463
1464 if (id >= MAX_CUSTOM_MODELS64 || !m->defined || m->curPartIndex >= m->numParts) return;
1465 part = &m->parts[m->curPartIndex];
1466
1467 p.min.X = GetFloat(data + 1);
1468 p.min.Y = GetFloat(data + 5);
1469 p.min.Z = GetFloat(data + 9);
1470 p.max.X = GetFloat(data + 13);
1471 p.max.Y = GetFloat(data + 17);
1472 p.max.Z = GetFloat(data + 21);
1473
1474 /* read u, v coords for our 6 faces */
1475 for (i = 0; i < 6; i++) {
1476 p.u1[i] = Stream_GetU16_BE(data + 25 + (i*8 + 0));
1477 p.v1[i] = Stream_GetU16_BE(data + 25 + (i*8 + 2));
1478 p.u2[i] = Stream_GetU16_BE(data + 25 + (i*8 + 4));
1479 p.v2[i] = Stream_GetU16_BE(data + 25 + (i*8 + 6));
1480 }
1481
1482 p.rotationOrigin.X = GetFloat(data + 73);
1483 p.rotationOrigin.Y = GetFloat(data + 77);
1484 p.rotationOrigin.Z = GetFloat(data + 81);
1485
1486 part->rotation.X = GetFloat(data + 85);
1487 part->rotation.Y = GetFloat(data + 89);
1488 part->rotation.Z = GetFloat(data + 93);
1489
1490 if (cpe_customModelsVer == 1) {
1491 /* ignore animations */
1492 p.flags = data[102];
1493 } else if (cpe_customModelsVer == 2) {
1494 p.flags = data[165];
1495
1496 data += 97;
1497 for (i = 0; i < MAX_CUSTOM_MODEL_ANIMS4; i++) {
1498 cc_uint8 tmp = *data++;
1499 part->anims[i].type = tmp & 0x3F;
1500 part->anims[i].axis = tmp >> 6;
1501
1502 part->anims[i].a = GetFloat(data);
1503 data += 4;
1504 part->anims[i].b = GetFloat(data);
1505 data += 4;
1506 part->anims[i].c = GetFloat(data);
1507 data += 4;
1508 part->anims[i].d = GetFloat(data);
1509 data += 4;
1510 }
1511 }
1512
1513 CustomModel_BuildPart(m, &p);
1514 m->curPartIndex++;
1515 if (m->curPartIndex == m->numParts) CustomModel_Register(m);
1516}
1517
1518/* unregisters and frees the custom model */
1519static void CPE_UndefineModel(cc_uint8* data) {
1520 cc_uint8 id = data[0];
1521 if (id < MAX_CUSTOM_MODELS64) CustomModel_Undefine(&custom_models[id]);
1522}
1523
1524static void CPE_Reset(void) {
1525 cpe_serverExtensionsCount = 0; cpe_pingTicks = 0;
1526 cpe_sendHeldBlock = false0; cpe_useMessageTypes = false0;
1527 cpe_envMapVer = 2; cpe_blockDefsExtVer = 2; cpe_customModelsVer = 2;
1528 cpe_needD3Fix = false0; cpe_extEntityPos = false0; cpe_twoWayPing = false0;
1529 cpe_extTextures = false0; cpe_fastMap = false0; cpe_extBlocks = false0;
1530 Game_UseCPEBlocks = false0; cpe_blockPerms = false0;
1531 if (!Game_UseCPE) return;
1532
1533 Net_Set(OPCODE_EXT_INFO, CPE_ExtInfo, 67)Protocol.Handlers[OPCODE_EXT_INFO] = CPE_ExtInfo; Protocol.Sizes
[OPCODE_EXT_INFO] = 67;
;
1534 Net_Set(OPCODE_EXT_ENTRY, CPE_ExtEntry, 69)Protocol.Handlers[OPCODE_EXT_ENTRY] = CPE_ExtEntry; Protocol.
Sizes[OPCODE_EXT_ENTRY] = 69;
;
1535 Net_Set(OPCODE_SET_REACH, CPE_SetClickDistance, 3)Protocol.Handlers[OPCODE_SET_REACH] = CPE_SetClickDistance; Protocol
.Sizes[OPCODE_SET_REACH] = 3;
;
1536 Net_Set(OPCODE_CUSTOM_BLOCK_LEVEL, CPE_CustomBlockLevel, 2)Protocol.Handlers[OPCODE_CUSTOM_BLOCK_LEVEL] = CPE_CustomBlockLevel
; Protocol.Sizes[OPCODE_CUSTOM_BLOCK_LEVEL] = 2;
;
1537 Net_Set(OPCODE_HOLD_THIS, CPE_HoldThis, 3)Protocol.Handlers[OPCODE_HOLD_THIS] = CPE_HoldThis; Protocol.
Sizes[OPCODE_HOLD_THIS] = 3;
;
1538 Net_Set(OPCODE_SET_TEXT_HOTKEY, CPE_SetTextHotkey, 134)Protocol.Handlers[OPCODE_SET_TEXT_HOTKEY] = CPE_SetTextHotkey
; Protocol.Sizes[OPCODE_SET_TEXT_HOTKEY] = 134;
;
1539
1540 Net_Set(OPCODE_EXT_ADD_PLAYER_NAME, CPE_ExtAddPlayerName, 196)Protocol.Handlers[OPCODE_EXT_ADD_PLAYER_NAME] = CPE_ExtAddPlayerName
; Protocol.Sizes[OPCODE_EXT_ADD_PLAYER_NAME] = 196;
;
1541 Net_Set(OPCODE_EXT_ADD_ENTITY, CPE_ExtAddEntity, 130)Protocol.Handlers[OPCODE_EXT_ADD_ENTITY] = CPE_ExtAddEntity; Protocol
.Sizes[OPCODE_EXT_ADD_ENTITY] = 130;
;
1542 Net_Set(OPCODE_EXT_REMOVE_PLAYER_NAME, CPE_ExtRemovePlayerName, 3)Protocol.Handlers[OPCODE_EXT_REMOVE_PLAYER_NAME] = CPE_ExtRemovePlayerName
; Protocol.Sizes[OPCODE_EXT_REMOVE_PLAYER_NAME] = 3;
;
1543
1544 Net_Set(OPCODE_ENV_SET_COLOR, CPE_SetEnvCol, 8)Protocol.Handlers[OPCODE_ENV_SET_COLOR] = CPE_SetEnvCol; Protocol
.Sizes[OPCODE_ENV_SET_COLOR] = 8;
;
1545 Net_Set(OPCODE_MAKE_SELECTION, CPE_MakeSelection, 86)Protocol.Handlers[OPCODE_MAKE_SELECTION] = CPE_MakeSelection;
Protocol.Sizes[OPCODE_MAKE_SELECTION] = 86;
;
1546 Net_Set(OPCODE_REMOVE_SELECTION, CPE_RemoveSelection, 2)Protocol.Handlers[OPCODE_REMOVE_SELECTION] = CPE_RemoveSelection
; Protocol.Sizes[OPCODE_REMOVE_SELECTION] = 2;
;
1547 Net_Set(OPCODE_SET_BLOCK_PERMISSION, CPE_SetBlockPermission, 4)Protocol.Handlers[OPCODE_SET_BLOCK_PERMISSION] = CPE_SetBlockPermission
; Protocol.Sizes[OPCODE_SET_BLOCK_PERMISSION] = 4;
;
1548 Net_Set(OPCODE_SET_MODEL, CPE_ChangeModel, 66)Protocol.Handlers[OPCODE_SET_MODEL] = CPE_ChangeModel; Protocol
.Sizes[OPCODE_SET_MODEL] = 66;
;
1549 Net_Set(OPCODE_ENV_SET_MAP_APPEARANCE, CPE_EnvSetMapAppearance, 69)Protocol.Handlers[OPCODE_ENV_SET_MAP_APPEARANCE] = CPE_EnvSetMapAppearance
; Protocol.Sizes[OPCODE_ENV_SET_MAP_APPEARANCE] = 69;
;
1550 Net_Set(OPCODE_ENV_SET_WEATHER, CPE_EnvWeatherType, 2)Protocol.Handlers[OPCODE_ENV_SET_WEATHER] = CPE_EnvWeatherType
; Protocol.Sizes[OPCODE_ENV_SET_WEATHER] = 2;
;
1551 Net_Set(OPCODE_HACK_CONTROL, CPE_HackControl, 8)Protocol.Handlers[OPCODE_HACK_CONTROL] = CPE_HackControl; Protocol
.Sizes[OPCODE_HACK_CONTROL] = 8;
;
1552 Net_Set(OPCODE_EXT_ADD_ENTITY2, CPE_ExtAddEntity2, 138)Protocol.Handlers[OPCODE_EXT_ADD_ENTITY2] = CPE_ExtAddEntity2
; Protocol.Sizes[OPCODE_EXT_ADD_ENTITY2] = 138;
;
1553
1554 Net_Set(OPCODE_BULK_BLOCK_UPDATE, CPE_BulkBlockUpdate, 1282)Protocol.Handlers[OPCODE_BULK_BLOCK_UPDATE] = CPE_BulkBlockUpdate
; Protocol.Sizes[OPCODE_BULK_BLOCK_UPDATE] = 1282;
;
1555 Net_Set(OPCODE_SET_TEXT_COLOR, CPE_SetTextColor, 6)Protocol.Handlers[OPCODE_SET_TEXT_COLOR] = CPE_SetTextColor; Protocol
.Sizes[OPCODE_SET_TEXT_COLOR] = 6;
;
1556 Net_Set(OPCODE_ENV_SET_MAP_URL, CPE_SetMapEnvUrl, 65)Protocol.Handlers[OPCODE_ENV_SET_MAP_URL] = CPE_SetMapEnvUrl;
Protocol.Sizes[OPCODE_ENV_SET_MAP_URL] = 65;
;
1557 Net_Set(OPCODE_ENV_SET_MAP_PROPERTY, CPE_SetMapEnvProperty, 6)Protocol.Handlers[OPCODE_ENV_SET_MAP_PROPERTY] = CPE_SetMapEnvProperty
; Protocol.Sizes[OPCODE_ENV_SET_MAP_PROPERTY] = 6;
;
1558 Net_Set(OPCODE_SET_ENTITY_PROPERTY, CPE_SetEntityProperty, 7)Protocol.Handlers[OPCODE_SET_ENTITY_PROPERTY] = CPE_SetEntityProperty
; Protocol.Sizes[OPCODE_SET_ENTITY_PROPERTY] = 7;
;
1559 Net_Set(OPCODE_TWO_WAY_PING, CPE_TwoWayPing, 4)Protocol.Handlers[OPCODE_TWO_WAY_PING] = CPE_TwoWayPing; Protocol
.Sizes[OPCODE_TWO_WAY_PING] = 4;
;
1560 Net_Set(OPCODE_SET_INVENTORY_ORDER, CPE_SetInventoryOrder, 3)Protocol.Handlers[OPCODE_SET_INVENTORY_ORDER] = CPE_SetInventoryOrder
; Protocol.Sizes[OPCODE_SET_INVENTORY_ORDER] = 3;
;
1561 Net_Set(OPCODE_SET_HOTBAR, CPE_SetHotbar, 3)Protocol.Handlers[OPCODE_SET_HOTBAR] = CPE_SetHotbar; Protocol
.Sizes[OPCODE_SET_HOTBAR] = 3;
;
1562 Net_Set(OPCODE_SET_SPAWNPOINT, CPE_SetSpawnPoint, 9)Protocol.Handlers[OPCODE_SET_SPAWNPOINT] = CPE_SetSpawnPoint;
Protocol.Sizes[OPCODE_SET_SPAWNPOINT] = 9;
;
1563 Net_Set(OPCODE_VELOCITY_CONTROL, CPE_VelocityControl, 16)Protocol.Handlers[OPCODE_VELOCITY_CONTROL] = CPE_VelocityControl
; Protocol.Sizes[OPCODE_VELOCITY_CONTROL] = 16;
;
1564 Net_Set(OPCODE_DEFINE_EFFECT, CPE_DefineEffect, 36)Protocol.Handlers[OPCODE_DEFINE_EFFECT] = CPE_DefineEffect; Protocol
.Sizes[OPCODE_DEFINE_EFFECT] = 36;
;
1565 Net_Set(OPCODE_SPAWN_EFFECT, CPE_SpawnEffect, 26)Protocol.Handlers[OPCODE_SPAWN_EFFECT] = CPE_SpawnEffect; Protocol
.Sizes[OPCODE_SPAWN_EFFECT] = 26;
;
1566 Net_Set(OPCODE_DEFINE_MODEL, CPE_DefineModel, 116)Protocol.Handlers[OPCODE_DEFINE_MODEL] = CPE_DefineModel; Protocol
.Sizes[OPCODE_DEFINE_MODEL] = 116;
;
1567 Net_Set(OPCODE_DEFINE_MODEL_PART, CPE_DefineModelPart, 104)Protocol.Handlers[OPCODE_DEFINE_MODEL_PART] = CPE_DefineModelPart
; Protocol.Sizes[OPCODE_DEFINE_MODEL_PART] = 104;
;
1568 Net_Set(OPCODE_UNDEFINE_MODEL, CPE_UndefineModel, 2)Protocol.Handlers[OPCODE_UNDEFINE_MODEL] = CPE_UndefineModel;
Protocol.Sizes[OPCODE_UNDEFINE_MODEL] = 2;
;
1569}
1570
1571static void CPE_Tick(void) {
1572 cpe_pingTicks++;
1573 if (cpe_pingTicks >= 20 && cpe_twoWayPing) {
1574 CPE_WriteTwoWayPing(false0, Ping_NextPingId());
1575 cpe_pingTicks = 0;
1576 }
1577}
1578
1579
1580/*########################################################################################################################*
1581*------------------------------------------------------Custom blocks------------------------------------------------------*
1582*#########################################################################################################################*/
1583static void BlockDefs_OnBlockUpdated(BlockID block, cc_bool didBlockLight) {
1584 if (!World.Loaded) return;
1585 /* Need to refresh lighting when a block's light blocking state changes */
1586 if (Blocks.BlocksLight[block] != didBlockLight) Lighting_Refresh();
1587}
1588
1589static TextureLoc BlockDefs_Tex(cc_uint8** ptr) {
1590 TextureLoc loc;
1591 cc_uint8* data = *ptr;
1592
1593 if (!cpe_extTextures) {
1594 loc = *data++;
1595 } else {
1596 loc = Stream_GetU16_BE(data) % ATLAS1D_MAX_ATLASES(16 * 32); data += 2;
1597 }
1598
1599 *ptr = data; return loc;
1600}
1601
1602static BlockID BlockDefs_DefineBlockCommonStart(cc_uint8** ptr, cc_bool uniqueSideTexs) {
1603 cc_string name;
1604 BlockID block;
1605 cc_bool didBlockLight;
1606 float speedLog2;
1607 cc_uint8 sound;
1608 cc_uint8* data = *ptr;
1609
1610 ReadBlock(data, block)if (cpe_extBlocks) { block = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { block = *data++; }
;
1611 didBlockLight = Blocks.BlocksLight[block];
1612 Block_ResetProps(block);
1613
1614 name = UNSAFE_GetString(data); data += STRING_SIZE64;
1615 Block_SetName(block, &name);
1616 Block_SetCollide(block, *data++);
1617
1618 speedLog2 = (*data++ - 128) / 64.0f;
1619 #define LOG_20.693147180559945 0.693147180559945
1620 Blocks.SpeedMultiplier[block] = (float)Math_Exp(LOG_20.693147180559945 * speedLog2); /* pow(2, x) */
1621
1622 Block_Tex(block, FACE_YMAX)Blocks.Textures[(block) * FACE_COUNT + (FACE_YMAX)] = BlockDefs_Tex(&data);
1623 if (uniqueSideTexs) {
1624 Block_Tex(block, FACE_XMIN)Blocks.Textures[(block) * FACE_COUNT + (FACE_XMIN)] = BlockDefs_Tex(&data);
1625 Block_Tex(block, FACE_XMAX)Blocks.Textures[(block) * FACE_COUNT + (FACE_XMAX)] = BlockDefs_Tex(&data);
1626 Block_Tex(block, FACE_ZMIN)Blocks.Textures[(block) * FACE_COUNT + (FACE_ZMIN)] = BlockDefs_Tex(&data);
1627 Block_Tex(block, FACE_ZMAX)Blocks.Textures[(block) * FACE_COUNT + (FACE_ZMAX)] = BlockDefs_Tex(&data);
1628 } else {
1629 Block_SetSide(BlockDefs_Tex(&data), block);
1630 }
1631 Block_Tex(block, FACE_YMIN)Blocks.Textures[(block) * FACE_COUNT + (FACE_YMIN)] = BlockDefs_Tex(&data);
1632
1633 Blocks.BlocksLight[block] = *data++ == 0;
1634 BlockDefs_OnBlockUpdated(block, didBlockLight);
1635
1636 sound = *data++;
1637 Blocks.StepSounds[block] = sound;
1638 Blocks.DigSounds[block] = sound;
1639 if (sound == SOUND_GLASS) Blocks.StepSounds[block] = SOUND_STONE;
1640
1641 Blocks.FullBright[block] = *data++ != 0;
1642 *ptr = data;
1643 return block;
1644}
1645
1646static void BlockDefs_DefineBlockCommonEnd(cc_uint8* data, cc_uint8 shape, BlockID block) {
1647 cc_uint8 draw = data[0];
1648 if (shape == 0) {
1649 Blocks.SpriteOffset[block] = draw;
1650 draw = DRAW_SPRITE;
1651 }
1652 Blocks.Draw[block] = draw;
1653
1654 Blocks.FogDensity[block] = data[1] == 0 ? 0.0f : (data[1] + 1) / 128.0f;
1655 Blocks.FogCol[block] = PackedCol_Make(data[2], data[3], data[4], 255)(((cc_uint8)(data[2]) << 0) | ((cc_uint8)(data[3]) <<
8) | ((cc_uint8)(data[4]) << 16) | ((cc_uint8)(255) <<
24))
;
1656 Block_DefineCustom(block);
1657}
1658
1659static void BlockDefs_DefineBlock(cc_uint8* data) {
1660 BlockID block = BlockDefs_DefineBlockCommonStart(&data, false0);
1661
1662 cc_uint8 shape = *data++;
1663 if (shape > 0 && shape <= 16) {
1664 Blocks.MaxBB[block].Y = shape / 16.0f;
1665 }
1666
1667 BlockDefs_DefineBlockCommonEnd(data, shape, block);
1668 /* Update sprite BoundingBox if necessary */
1669 if (Blocks.Draw[block] == DRAW_SPRITE) Block_RecalculateBB(block);
1670}
1671
1672static void BlockDefs_UndefineBlock(cc_uint8* data) {
1673 BlockID block;
1674 cc_bool didBlockLight;
1675
1676 ReadBlock(data, block)if (cpe_extBlocks) { block = Stream_GetU16_BE(data) % BLOCK_COUNT
; data += 2;} else { block = *data++; }
;
1677 didBlockLight = Blocks.BlocksLight[block];
1678
1679 Block_ResetProps(block);
1680 BlockDefs_OnBlockUpdated(block, didBlockLight);
1681 Block_UpdateCulling(block);
1682
1683 Inventory_Remove(block);
1684 if (block < BLOCK_CPE_COUNT) { Inventory_AddDefault(block); }
1685
1686 Block_SetCustomDefined(block, false0);
1687 Event_RaiseVoid(&BlockEvents.BlockDefChanged);
1688 /* Update sprite BoundingBox if necessary */
1689 if (Blocks.Draw[block] == DRAW_SPRITE) Block_RecalculateBB(block);
1690}
1691
1692static void BlockDefs_DefineBlockExt(cc_uint8* data) {
1693 Vec3 minBB, maxBB;
1694 BlockID block = BlockDefs_DefineBlockCommonStart(&data, cpe_blockDefsExtVer >= 2);
1695
1696 minBB.X = (cc_int8)(*data++) / 16.0f;
1697 minBB.Y = (cc_int8)(*data++) / 16.0f;
1698 minBB.Z = (cc_int8)(*data++) / 16.0f;
1699
1700 maxBB.X = (cc_int8)(*data++) / 16.0f;
1701 maxBB.Y = (cc_int8)(*data++) / 16.0f;
1702 maxBB.Z = (cc_int8)(*data++) / 16.0f;
1703
1704 Blocks.MinBB[block] = minBB;
1705 Blocks.MaxBB[block] = maxBB;
1706 BlockDefs_DefineBlockCommonEnd(data, 1, block);
1707}
1708
1709static void BlockDefs_Reset(void) {
1710 if (!Game_UseCPE || !Game_AllowCustomBlocks) return;
1711 Net_Set(OPCODE_DEFINE_BLOCK, BlockDefs_DefineBlock, 80)Protocol.Handlers[OPCODE_DEFINE_BLOCK] = BlockDefs_DefineBlock
; Protocol.Sizes[OPCODE_DEFINE_BLOCK] = 80;
;
1712 Net_Set(OPCODE_UNDEFINE_BLOCK, BlockDefs_UndefineBlock, 2)Protocol.Handlers[OPCODE_UNDEFINE_BLOCK] = BlockDefs_UndefineBlock
; Protocol.Sizes[OPCODE_UNDEFINE_BLOCK] = 2;
;
1713 Net_Set(OPCODE_DEFINE_BLOCK_EXT, BlockDefs_DefineBlockExt, 85)Protocol.Handlers[OPCODE_DEFINE_BLOCK_EXT] = BlockDefs_DefineBlockExt
; Protocol.Sizes[OPCODE_DEFINE_BLOCK_EXT] = 85;
;
1714}
1715
1716
1717/*########################################################################################################################*
1718*-----------------------------------------------------Public handlers-----------------------------------------------------*
1719*#########################################################################################################################*/
1720static void Protocol_Reset(void) {
1721 Classic_Reset();
1722 CPE_Reset();
1723 BlockDefs_Reset();
1724 WoM_Reset();
1725}
1726
1727void Protocol_Tick(void) {
1728 Classic_Tick();
1729 CPE_Tick();
1730 WoM_Tick();
1731}
1732
1733static void OnInit(void) {
1734 if (Server.IsSinglePlayer) return;
1735 Protocol_Reset();
1736}
1737
1738static void OnReset(void) {
1739 if (Server.IsSinglePlayer) return;
1740 Mem_Set(&Protocol, 0, sizeof(Protocol));
1741 Protocol_Reset();
1742 FreeMapStates();
1743}
1744
1745struct IGameComponent Protocol_Component = {
1746 OnInit, /* Init */
1747 NULL((void*)0), /* Free */
1748 OnReset, /* Reset */
1749};