Bug Summary

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