From ebb45d00a2bad3b7b109ff5fcad64f3d1857bd37 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 11 Aug 2023 11:35:03 +1000 Subject: [PATCH] Properly support CPE extension versions > 1 --- src/Protocol.c | 239 +++++++++++++++++++++++++++++-------------------- 1 file changed, 142 insertions(+), 97 deletions(-) diff --git a/src/Protocol.c b/src/Protocol.c index 4c1266247..5e226a78a 100644 --- a/src/Protocol.c +++ b/src/Protocol.c @@ -41,12 +41,71 @@ static cc_uint64 map_receiveBeg; static struct Stream map_part; static int map_volume; -/* CPE state */ +/*########################################################################################################################* +*-----------------------------------------------------CPE extensions------------------------------------------------------* +*#########################################################################################################################*/ +struct CpeExt { + const char* name; + cc_uint8 clientVersion, serverVersion; +}; cc_bool cpe_needD3Fix; static int cpe_serverExtensionsCount, cpe_pingTicks; -static int cpe_envMapVer = 2, cpe_blockDefsExtVer = 2, cpe_customModelsVer = 2; -static cc_bool cpe_sendHeldBlock, cpe_useMessageTypes, cpe_extEntityPos, cpe_blockPerms, cpe_fastMap; -static cc_bool cpe_twoWayPing, cpe_pluginMessages, cpe_extTextures, cpe_extBlocks; + +static struct CpeExt + clickDist_Ext = { "ClickDistance", 1 }, + customBlocks_Ext = { "CustomBlocks", 1 }, + heldBlock_Ext = { "HeldBlock", 1 }, + emoteFix_Ext = { "EmoteFix", 1 }, + textHotKey_Ext = { "TextHotKey", 1 }, + extPlayerList_Ext = { "ExtPlayerList", 2 }, + envColors_Ext = { "EnvColors", 1 }, + selectionCuboid_Ext = { "SelectionCuboid", 1 }, + blockPerms_Ext = { "BlockPermissions", 1 }, + changeModel_Ext = { "ChangeModel", 2 }, + mapAppearance_Ext = { "EnvMapAppearance", 2 }, + weatherType_Ext = { "EnvWeatherType", 1 }, + messageTypes_Ext = { "MessageTypes", 1 }, + hackControl_Ext = { "HackControl", 1 }, + playerClick_Ext = { "PlayerClick", 1 }, + fullCP437_Ext = { "FullCP437", 1 }, + longerMessages_Ext = { "LongerMessages", 1 }, + blockDefs_Ext = { "BlockDefinitions", 1 }, + blockDefsExt_Ext = { "BlockDefinitionsExt", 2 }, + bulkBlockUpdate_Ext = { "BulkBlockUpdate", 1 }, + textColors_Ext = { "TextColors", 1 }, + envMapAspect_Ext = { "EnvMapAspect", 1 }, + entityProperty_Ext = { "EntityProperty", 1 }, + extEntityPos_Ext = { "ExtEntityPositions", 1 }, + twoWayPing_Ext = { "TwoWayPing", 1 }, + invOrder_Ext = { "InventoryOrder", 1 }, + instantMOTD_Ext = { "InstantMOTD", 1 }, + fastMap_Ext = { "FastMap", 1 }, + setHotbar_Ext = { "SetHotbar", 1 }, + setSpawnpoint_Ext = { "SetSpawnpoint", 1 }, + velControl_Ext = { "VelocityControl", 1 }, + customParticles_Ext = { "CustomParticles", 1 }, + customModels_Ext = { "CustomModels", 2 }, + pluginMessages_Ext = { "PluginMessages", 1 }, + extTeleport_Ext = { "ExtEntityTeleport", 1 }, + extTextures_Ext = { "ExtendedTextures", 1 }, + extBlocks_Ext = { "ExtendedBlocks", 1 }; + +static struct CpeExt* cpe_clientExtensions[] = { + &clickDist_Ext, &customBlocks_Ext, &heldBlock_Ext, &emoteFix_Ext, &textHotKey_Ext, &extPlayerList_Ext, + &envColors_Ext, &selectionCuboid_Ext, &blockPerms_Ext, &changeModel_Ext, &mapAppearance_Ext, &weatherType_Ext, + &messageTypes_Ext, &hackControl_Ext, &playerClick_Ext, &fullCP437_Ext, &longerMessages_Ext, &blockDefs_Ext, + &blockDefsExt_Ext, &bulkBlockUpdate_Ext, &textColors_Ext, &envMapAspect_Ext, &entityProperty_Ext, &extEntityPos_Ext, + &twoWayPing_Ext, &invOrder_Ext, &fastMap_Ext, &setHotbar_Ext, &setSpawnpoint_Ext, &velControl_Ext, + &customParticles_Ext, &customModels_Ext, &pluginMessages_Ext, &extTeleport_Ext, +#ifdef EXTENDED_TEXTURES + &extTextures_Ext, +#endif +#ifdef EXTENDED_BLOCKS + &extBlocks_Ext, +#endif +}; +#define IsSupported(ext) (ext.serverVersion > 0) + /*########################################################################################################################* *-----------------------------------------------------Common handlers-----------------------------------------------------* @@ -56,7 +115,7 @@ static cc_bool cpe_twoWayPing, cpe_pluginMessages, cpe_extTextures, cpe_extBlock #define ReadBlock(data, value) value = *data++; #else #define ReadBlock(data, value)\ -if (cpe_extBlocks) {\ +if (IsSupported(extBlocks_Ext)) {\ value = Stream_GetU16_BE(data) % BLOCK_COUNT; data += 2;\ } else { value = *data++; } #endif @@ -65,7 +124,7 @@ if (cpe_extBlocks) {\ #define WriteBlock(data, value) *data++ = value; #else #define WriteBlock(data, value)\ -if (cpe_extBlocks) {\ +if (IsSupported(extBlocks_Ext)) {\ Stream_SetU16_BE(data, value); data += 2;\ } else { *data++ = (BlockRaw)value; } #endif @@ -175,7 +234,7 @@ static void UpdateLocation(EntityID id, struct LocationUpdate* update) { static void UpdateUserType(struct HacksComp* hacks, cc_uint8 value) { cc_bool isOp = value >= 100 && value <= 127; hacks->IsOp = isOp; - if (cpe_blockPerms) return; + if (IsSupported(blockPerms_Ext)) return; Blocks.CanPlace[BLOCK_BEDROCK] = isOp; Blocks.CanDelete[BLOCK_BEDROCK] = isOp; @@ -258,7 +317,7 @@ static void WoM_ParseConfig(struct HttpRequest* item) { if (Convert_ParseInt(&value, &waterLevel)) { Env_SetEdgeHeight(waterLevel); } - } else if (String_CaselessEqualsConst(&key, "user.detail") && !cpe_useMessageTypes) { + } else if (String_CaselessEqualsConst(&key, "user.detail") && !IsSupported(messageTypes_Ext)) { Chat_AddOf(&value, MSG_TYPE_STATUS_2); } } @@ -407,13 +466,13 @@ static cc_uint8* Classic_WritePosition(cc_uint8* data, Vec3 pos, float yaw, floa *data++ = OPCODE_ENTITY_TELEPORT; { - payload = cpe_sendHeldBlock ? Inventory_SelectedBlock : ENTITIES_SELF_ID; + payload = IsSupported(heldBlock_Ext) ? Inventory_SelectedBlock : ENTITIES_SELF_ID; WriteBlock(data, payload); x = (int)(pos.X * 32); y = (int)(pos.Y * 32) + 51; z = (int)(pos.Z * 32); - if (cpe_extEntityPos) { + if (IsSupported(extEntityPos_Ext)) { Stream_SetU32_BE(data, x); data += 4; Stream_SetU32_BE(data, y); data += 4; Stream_SetU32_BE(data, z); data += 4; @@ -486,7 +545,7 @@ static void Classic_LevelInit(cc_uint8* data) { if (map_begunLoading) return; Classic_StartLoading(); - if (!cpe_fastMap) return; + if (!IsSupported(fastMap_Ext)) return; /* Fast map puts volume in header, and uses raw DEFLATE without GZIP header/footer */ map_volume = Stream_GetU32_BE(data); @@ -515,7 +574,7 @@ static void Classic_LevelDataChunk(cc_uint8* data) { m = &map1; #else /* progress byte in original classic, but we ignore it */ - if (cpe_extBlocks && data[1026]) { + if (IsSupported(extBlocks_Ext) && data[1026]) { m = &map2; } else { m = &map1; @@ -568,7 +627,9 @@ static void Classic_LevelFinalise(cc_uint8* data) { #ifdef EXTENDED_BLOCKS /* defer allocation of second map array if possible */ - if (cpe_extBlocks && map2.blocks) World_SetMapUpper(map2.blocks); + if (IsSupported(extBlocks_Ext) && map2.blocks) { + World_SetMapUpper(map2.blocks); + } map2.blocks = NULL; #endif World_SetNewMap(map1.blocks, width, height, length); @@ -662,7 +723,7 @@ static void Classic_Message(cc_uint8* data) { String_InitArray(text, textBuffer); /* Original vanilla server uses player ids for type, 255 for server messages (&e prefix) */ - if (!cpe_useMessageTypes) { + if (!IsSupported(messageTypes_Ext)) { if (type == 0xFF) String_AppendConst(&text, "&e"); type = MSG_TYPE_NORMAL; } @@ -694,7 +755,7 @@ static void Classic_ReadAbsoluteLocation(cc_uint8* data, EntityID id, cc_uint8 f int x, y, z; cc_uint8 mode; - if (cpe_extEntityPos) { + if (IsSupported(extEntityPos_Ext)) { x = (int)Stream_GetU32_BE(&data[0]); y = (int)Stream_GetU32_BE(&data[4]); z = (int)Stream_GetU32_BE(&data[8]); @@ -765,16 +826,29 @@ static cc_uint8* Classic_Tick(cc_uint8* data) { /*########################################################################################################################* *------------------------------------------------------CPE protocol-------------------------------------------------------* *#########################################################################################################################*/ -static const char* cpe_clientExtensions[] = { - "ClickDistance", "CustomBlocks", "HeldBlock", "EmoteFix", "TextHotKey", "ExtPlayerList", - "EnvColors", "SelectionCuboid", "BlockPermissions", "ChangeModel", "EnvMapAppearance", - "EnvWeatherType", "MessageTypes", "HackControl", "PlayerClick", "FullCP437", "LongerMessages", - "BlockDefinitions", "BlockDefinitionsExt", "BulkBlockUpdate", "TextColors", "EnvMapAspect", - "EntityProperty", "ExtEntityPositions", "TwoWayPing", "InventoryOrder", "InstantMOTD", "FastMap", "SetHotbar", - "SetSpawnpoint", "VelocityControl", "CustomParticles", "CustomModels", "PluginMessages", "ExtEntityTeleport", - /* NOTE: These must be placed last for when EXTENDED_TEXTURES or EXTENDED_BLOCKS are not defined */ - "ExtendedTextures", "ExtendedBlocks" -}; + +static void CPEExtensions_Reset(void) { + struct CpeExt* ext; + int i; + + for (i = 0; i < Array_Elems(cpe_clientExtensions); i++) + { + ext = cpe_clientExtensions[i]; + ext->serverVersion = 0; + } +} + +static struct CpeExt* CPEExtensions_Find(const cc_string* name) { + struct CpeExt* ext; + int i; + + for (i = 0; i < Array_Elems(cpe_clientExtensions); i++) + { + ext = cpe_clientExtensions[i]; + if (String_CaselessEqualsConst(name, ext->name)) return ext; + } + return NULL; +} static void CPE_SetMapEnvUrl(cc_uint8* data); #define Ext_Deg2Packed(x) ((int)((x) * 65536.0f / 360.0f)) @@ -810,8 +884,7 @@ void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct void CPE_SendPluginMessage(cc_uint8 channel, cc_uint8* data) { cc_uint8 buffer[66]; - - if (!cpe_pluginMessages) return; + if (!IsSupported(pluginMessages_Ext)) return; buffer[0] = OPCODE_PLUGIN_MESSAGE; { @@ -851,18 +924,12 @@ static cc_uint8* CPE_WriteTwoWayPing(cc_uint8* data, cc_bool serverToClient, int } static void CPE_SendCpeExtInfoReply(void) { + struct CpeExt* ext; int count = Array_Elems(cpe_clientExtensions); cc_string name; int i, ver; if (cpe_serverExtensionsCount) return; - -#ifndef EXTENDED_TEXTURES - count--; -#endif -#ifndef EXTENDED_BLOCKS - count--; -#endif #ifdef EXTENDED_BLOCKS if (!Game_AllowCustomBlocks) count -= 3; @@ -871,14 +938,12 @@ static void CPE_SendCpeExtInfoReply(void) { #endif CPE_SendExtInfo(count); - for (i = 0; i < Array_Elems(cpe_clientExtensions); i++) { - name = String_FromReadonly(cpe_clientExtensions[i]); - ver = 1; - - if (String_CaselessEqualsConst(&name, "ExtPlayerList")) ver = 2; - if (String_CaselessEqualsConst(&name, "EnvMapAppearance")) ver = cpe_envMapVer; - if (String_CaselessEqualsConst(&name, "BlockDefinitionsExt")) ver = cpe_blockDefsExtVer; - if (String_CaselessEqualsConst(&name, "CustomModels")) ver = cpe_customModelsVer; + for (i = 0; i < Array_Elems(cpe_clientExtensions); i++) + { + ext = cpe_clientExtensions[i]; + name = String_FromReadonly(ext->name); + ver = ext->serverVersion; + /* Don't reply with version higher than what server supports to workaround some buggy server software */ if (!Game_AllowCustomBlocks) { if (String_CaselessEqualsConst(&name, "BlockDefinitionsExt")) continue; @@ -887,13 +952,6 @@ static void CPE_SendCpeExtInfoReply(void) { if (String_CaselessEqualsConst(&name, "ExtendedBlocks")) continue; #endif } - -#ifndef EXTENDED_TEXTURES - if (String_CaselessEqualsConst(&name, "ExtendedTextures")) continue; -#endif -#ifndef EXTENDED_BLOCKS - if (String_CaselessEqualsConst(&name, "ExtendedBlocks")) continue; -#endif CPE_SendExtEntry(&name, ver); } } @@ -911,67 +969,55 @@ static void CPE_ExtInfo(cc_uint8* data) { } static void CPE_ExtEntry(cc_uint8* data) { - cc_string ext = UNSAFE_GetString(data); - int version = data[67]; - Platform_Log2("cpe ext: %s, %i", &ext, &version); + struct CpeExt* ext; + cc_string name = UNSAFE_GetString(data); + int version = data[67]; + Platform_Log2("cpe ext: %s, %i", &name, &version); cpe_serverExtensionsCount--; - CPE_SendCpeExtInfoReply(); + CPE_SendCpeExtInfoReply(); + + ext = CPEExtensions_Find(&name); + if (!ext) return; + ext->serverVersion = min(ext->clientVersion, version); /* update support state */ - if (String_CaselessEqualsConst(&ext, "HeldBlock")) { - cpe_sendHeldBlock = true; - } else if (String_CaselessEqualsConst(&ext, "MessageTypes")) { - cpe_useMessageTypes = true; - } else if (String_CaselessEqualsConst(&ext, "ExtPlayerList")) { + if (ext == &extPlayerList_Ext) { Server.SupportsExtPlayerList = true; - } else if (String_CaselessEqualsConst(&ext, "BlockPermissions")) { - cpe_blockPerms = true; - } else if (String_CaselessEqualsConst(&ext, "PlayerClick")) { + } else if (ext == &playerClick_Ext) { Server.SupportsPlayerClick = true; - } else if (String_CaselessEqualsConst(&ext, "EnvMapAppearance")) { - cpe_envMapVer = version; - if (version == 1) return; + } else if (ext == &mapAppearance_Ext) { + if (ext->serverVersion == 1) return; Protocol.Sizes[OPCODE_ENV_SET_MAP_APPEARANCE] += 4; - } else if (String_CaselessEqualsConst(&ext, "LongerMessages")) { + } else if (ext == &longerMessages_Ext) { Server.SupportsPartialMessages = true; - } else if (String_CaselessEqualsConst(&ext, "FullCP437")) { + } else if (ext == &fullCP437_Ext) { Server.SupportsFullCP437 = true; - } else if (String_CaselessEqualsConst(&ext, "BlockDefinitionsExt")) { - cpe_blockDefsExtVer = version; - if (version == 1) return; + } else if (ext == &blockDefsExt_Ext) { + if (ext->serverVersion == 1) return; Protocol.Sizes[OPCODE_DEFINE_BLOCK_EXT] += 3; - } else if (String_CaselessEqualsConst(&ext, "ExtEntityPositions")) { + } else if (ext == &extEntityPos_Ext) { Protocol.Sizes[OPCODE_ENTITY_TELEPORT] += 6; Protocol.Sizes[OPCODE_ADD_ENTITY] += 6; Protocol.Sizes[OPCODE_EXT_ADD_ENTITY2] += 6; Protocol.Sizes[OPCODE_SET_SPAWNPOINT] += 6; Protocol.Sizes[OPCODE_ENTITY_TELEPORT_EXT] += 6; - cpe_extEntityPos = true; - } else if (String_CaselessEqualsConst(&ext, "TwoWayPing")) { - cpe_twoWayPing = true; - } else if (String_CaselessEqualsConst(&ext, "FastMap")) { + } else if (ext == &fastMap_Ext) { Protocol.Sizes[OPCODE_LEVEL_BEGIN] += 4; - cpe_fastMap = true; - } else if (String_CaselessEqualsConst(&ext, "CustomModels")) { - cpe_customModelsVer = min(2, version); - if (version == 2) { + } else if (ext == &customModels_Ext) { + if (ext->serverVersion == 2) { Protocol.Sizes[OPCODE_DEFINE_MODEL_PART] = 167; } - } else if (String_CaselessEqualsConst(&ext, "PluginMessages")) { - cpe_pluginMessages = true; } #ifdef EXTENDED_TEXTURES - else if (String_CaselessEqualsConst(&ext, "ExtendedTextures")) { + else if (ext == &extTextures_Ext) { Protocol.Sizes[OPCODE_DEFINE_BLOCK] += 3; Protocol.Sizes[OPCODE_DEFINE_BLOCK_EXT] += 6; - cpe_extTextures = true; } #endif #ifdef EXTENDED_BLOCKS - else if (String_CaselessEqualsConst(&ext, "ExtendedBlocks")) { + else if (ext == &extBlocks_Ext) { if (!Game_AllowCustomBlocks) return; - cpe_extBlocks = true; Protocol.Sizes[OPCODE_SET_BLOCK] += 1; Protocol.Sizes[OPCODE_HOLD_THIS] += 1; @@ -1140,7 +1186,7 @@ static void CPE_EnvSetMapAppearance(cc_uint8* data) { Env_SetSidesBlock(data[64]); Env_SetEdgeBlock(data[65]); Env_SetEdgeHeight((cc_int16)Stream_GetU16_BE(data + 66)); - if (cpe_envMapVer == 1) return; + if (mapAppearance_Ext.serverVersion == 1) return; /* Version 2 */ Env_SetCloudsHeight((cc_int16)Stream_GetU16_BE(data + 68)); @@ -1203,7 +1249,7 @@ static void CPE_BulkBlockUpdate(cc_uint8* data) { } data += BULK_MAX_BLOCKS; - if (cpe_extBlocks) { + if (IsSupported(extBlocks_Ext)) { for (i = 0; i < count; i += 4) { cc_uint8 flags = data[i >> 2]; blocks[i + 0] |= (BlockID)((flags & 0x03) << 8); @@ -1363,7 +1409,7 @@ static void CPE_SetSpawnPoint(cc_uint8* data) { struct LocalPlayer* p = &LocalPlayer_Instance; int x, y, z; - if (cpe_extEntityPos) { + if (IsSupported(extEntityPos_Ext)) { x = (int)Stream_GetU32_BE(&data[0]); y = (int)Stream_GetU32_BE(&data[4]); z = (int)Stream_GetU32_BE(&data[8]); @@ -1522,10 +1568,10 @@ static void CPE_DefineModelPart(cc_uint8* data) { part->rotation.Y = GetFloat(data + 89); part->rotation.Z = GetFloat(data + 93); - if (cpe_customModelsVer == 1) { + if (customModels_Ext.serverVersion == 1) { /* ignore animations */ p.flags = data[102]; - } else if (cpe_customModelsVer == 2) { + } else { p.flags = data[165]; data += 97; @@ -1583,11 +1629,9 @@ static void CPE_ExtEntityTeleport(cc_uint8* data) { static void CPE_Reset(void) { cpe_serverExtensionsCount = 0; cpe_pingTicks = 0; - cpe_sendHeldBlock = false; cpe_useMessageTypes = false; - cpe_envMapVer = 2; cpe_blockDefsExtVer = 2; cpe_customModelsVer = 2; - cpe_needD3Fix = false; cpe_extEntityPos = false; cpe_twoWayPing = false; - cpe_pluginMessages = false; cpe_extTextures = false; cpe_fastMap = false; - cpe_extBlocks = false; Game_UseCPEBlocks = false; cpe_blockPerms = false; + CPEExtensions_Reset(); + cpe_needD3Fix = false; + Game_UseCPEBlocks = false; if (!Game_Version.HasCPE) return; Net_Set(OPCODE_EXT_INFO, CPE_ExtInfo, 67); @@ -1632,7 +1676,7 @@ static void CPE_Reset(void) { static cc_uint8* CPE_Tick(cc_uint8* data) { cpe_pingTicks++; - if (cpe_pingTicks >= 20 && cpe_twoWayPing) { + if (cpe_pingTicks >= 20 && IsSupported(twoWayPing_Ext)) { data = CPE_WriteTwoWayPing(data, false, Ping_NextPingId()); cpe_pingTicks = 0; } @@ -1653,7 +1697,7 @@ static TextureLoc BlockDefs_Tex(cc_uint8** ptr) { TextureLoc loc; cc_uint8* data = *ptr; - if (!cpe_extTextures) { + if (!IsSupported(extTextures_Ext)) { loc = *data++; } else { loc = Stream_GetU16_BE(data) % ATLAS1D_MAX_ATLASES; data += 2; @@ -1743,7 +1787,8 @@ static void BlockDefs_UndefineBlock(cc_uint8* data) { static void BlockDefs_DefineBlockExt(cc_uint8* data) { Vec3 minBB, maxBB; - BlockID block = BlockDefs_DefineBlockCommonStart(&data, cpe_blockDefsExtVer >= 2); + BlockID block = BlockDefs_DefineBlockCommonStart(&data, + blockDefsExt_Ext.serverVersion >= 2); minBB.X = (cc_int8)(*data++) / 16.0f; minBB.Y = (cc_int8)(*data++) / 16.0f;