diff --git a/ClassicalSharp/Entities/Components/CollisionsComponent.cs b/ClassicalSharp/Entities/Components/CollisionsComponent.cs index e6a912cf7..8b62ced00 100644 --- a/ClassicalSharp/Entities/Components/CollisionsComponent.cs +++ b/ClassicalSharp/Entities/Components/CollisionsComponent.cs @@ -49,13 +49,8 @@ namespace ClassicalSharp.Entities { for (int i = 0; i < count; i++) { // Unpack the block and coordinate data State state = Searcher.stateCache[i]; - #if !ONLY_8BIT bPos.X = state.X >> 3; bPos.Y = state.Y >> 4; bPos.Z = state.Z >> 3; int block = (state.X & 0x7) | (state.Y & 0xF) << 3 | (state.Z & 0x7) << 7; - #else - bPos.X = state.X >> 3; bPos.Y = state.Y >> 3; bPos.Z = state.Z >> 3; - int block = (state.X & 0x7) | (state.Y & 0x7) << 3 | (state.Z & 0x7) << 6; - #endif blockBB.Min = BlockInfo.MinBB[block]; blockBB.Min.X += bPos.X; blockBB.Min.Y += bPos.Y; blockBB.Min.Z += bPos.Z; diff --git a/ClassicalSharp/Math/Physics/Searcher.cs b/ClassicalSharp/Math/Physics/Searcher.cs index d826d65cd..34142464c 100644 --- a/ClassicalSharp/Math/Physics/Searcher.cs +++ b/ClassicalSharp/Math/Physics/Searcher.cs @@ -1,125 +1,119 @@ -// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 -using ClassicalSharp.Entities; -using ClassicalSharp.Map; -using System; -using OpenTK; -using BlockID = System.UInt16; - -namespace ClassicalSharp.Physics { - - public struct State { public int X, Y, Z; public float tSquared; } - - /// Calculates all possible blocks that a moving entity can intersect with. - public sealed class Searcher { - - public static State[] stateCache = new State[0]; - - public static int FindReachableBlocks(Game game, Entity entity, - out AABB entityBB, out AABB entityExtentBB) { - Vector3 vel = entity.Velocity; - entityBB = entity.Bounds; - - // Exact maximum extent the entity can reach, and the equivalent map coordinates. - entityExtentBB = new AABB( - vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X, - vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y, - vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z, - vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X, - vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y, - vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z - ); - Vector3I min = Vector3I.Floor(entityExtentBB.Min); - Vector3I max = Vector3I.Floor(entityExtentBB.Max); - - int elements = (max.X + 1 - min.X) * (max.Y + 1 - min.Y) * (max.Z + 1 - min.Z); - if (elements > stateCache.Length) { - stateCache = new State[elements]; - } - - AABB blockBB; - State state; - int count = 0; - - // Order loops so that we minimise cache misses - for (int y = min.Y; y <= max.Y; y++) - for (int z = min.Z; z <= max.Z; z++) - for (int x = min.X; x <= max.X; x++) - { - BlockID block = game.World.GetPhysicsBlock(x, y, z); - if (BlockInfo.Collide[block] != CollideType.Solid) continue; - - blockBB.Min = BlockInfo.MinBB[block]; - blockBB.Min.X += x; blockBB.Min.Y += y; blockBB.Min.Z += z; - blockBB.Max = BlockInfo.MaxBB[block]; - blockBB.Max.X += x; blockBB.Max.Y += y; blockBB.Max.Z += z; - - if (!entityExtentBB.Intersects(blockBB)) continue; // necessary for non whole blocks. (slabs) - - float tx, ty, tz; - CalcTime(ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz); - if (tx > 1 || ty > 1 || tz > 1) continue; - - #if !ONLY_8BIT - state.X = (x << 3) | (block & 0x007); - state.Y = (y << 4) | ((block & 0x078) >> 3); - state.Z = (z << 3) | ((block & 0x380) >> 7); - #else - state.X = (x << 3) | (block & 0x007); - state.Y = (y << 3) | ((block & 0x038) >> 3); - state.Z = (z << 3) | ((block & 0x1C0) >> 6); - #endif - state.tSquared = tx * tx + ty * ty + tz * tz; - - stateCache[count++] = state; - } - - if (count > 0) - QuickSort(stateCache, 0, count - 1); - return count; - } - - - public static void CalcTime(ref Vector3 vel, ref AABB entityBB, ref AABB blockBB, - out float tx, out float ty, out float tz) { - float dx = vel.X > 0 ? blockBB.Min.X - entityBB.Max.X : entityBB.Min.X - blockBB.Max.X; - float dy = vel.Y > 0 ? blockBB.Min.Y - entityBB.Max.Y : entityBB.Min.Y - blockBB.Max.Y; - float dz = vel.Z > 0 ? blockBB.Min.Z - entityBB.Max.Z : entityBB.Min.Z - blockBB.Max.Z; - - tx = vel.X == 0 ? float.PositiveInfinity : Math.Abs(dx / vel.X); - ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs(dy / vel.Y); - tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs(dz / vel.Z); - - if (entityBB.Max.X >= blockBB.Min.X && entityBB.Min.X <= blockBB.Max.X) tx = 0; // Inlined XIntersects - if (entityBB.Max.Y >= blockBB.Min.Y && entityBB.Min.Y <= blockBB.Max.Y) ty = 0; // Inlined YIntersects - if (entityBB.Max.Z >= blockBB.Min.Z && entityBB.Min.Z <= blockBB.Max.Z) tz = 0; // Inlined ZIntersects - } - - static void QuickSort(State[] keys, int left, int right) { - while (left < right) { - int i = left, j = right; - float pivot = keys[(i + j) / 2].tSquared; - // partition the list - while (i <= j) { - while (pivot > keys[i].tSquared) i++; - while (pivot < keys[j].tSquared) j--; - - if (i <= j) { - State key = keys[i]; keys[i] = keys[j]; keys[j] = key; - i++; j--; - } - } - - // recurse into the smaller subset - if (j - left <= right - i) { - if (left < j) - QuickSort(keys, left, j); - left = i; - } else { - if (i < right) - QuickSort(keys, i, right); - right = j; - } - } - } - } +// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 +using ClassicalSharp.Entities; +using ClassicalSharp.Map; +using System; +using OpenTK; +using BlockID = System.UInt16; + +namespace ClassicalSharp.Physics { + + public struct State { public int X, Y, Z; public float tSquared; } + + /// Calculates all possible blocks that a moving entity can intersect with. + public sealed class Searcher { + + public static State[] stateCache = new State[0]; + + public static int FindReachableBlocks(Game game, Entity entity, + out AABB entityBB, out AABB entityExtentBB) { + Vector3 vel = entity.Velocity; + entityBB = entity.Bounds; + + // Exact maximum extent the entity can reach, and the equivalent map coordinates. + entityExtentBB = new AABB( + vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X, + vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y, + vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z, + vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X, + vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y, + vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z + ); + Vector3I min = Vector3I.Floor(entityExtentBB.Min); + Vector3I max = Vector3I.Floor(entityExtentBB.Max); + + int elements = (max.X + 1 - min.X) * (max.Y + 1 - min.Y) * (max.Z + 1 - min.Z); + if (elements > stateCache.Length) { + stateCache = new State[elements]; + } + + AABB blockBB; + State state; + int count = 0; + + // Order loops so that we minimise cache misses + for (int y = min.Y; y <= max.Y; y++) + for (int z = min.Z; z <= max.Z; z++) + for (int x = min.X; x <= max.X; x++) + { + BlockID block = game.World.GetPhysicsBlock(x, y, z); + if (BlockInfo.Collide[block] != CollideType.Solid) continue; + + blockBB.Min = BlockInfo.MinBB[block]; + blockBB.Min.X += x; blockBB.Min.Y += y; blockBB.Min.Z += z; + blockBB.Max = BlockInfo.MaxBB[block]; + blockBB.Max.X += x; blockBB.Max.Y += y; blockBB.Max.Z += z; + + if (!entityExtentBB.Intersects(blockBB)) continue; // necessary for non whole blocks. (slabs) + + float tx, ty, tz; + CalcTime(ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz); + if (tx > 1 || ty > 1 || tz > 1) continue; + + state.X = (x << 3) | (block & 0x007); + state.Y = (y << 4) | ((block & 0x078) >> 3); + state.Z = (z << 3) | ((block & 0x380) >> 7); + state.tSquared = tx * tx + ty * ty + tz * tz; + + stateCache[count++] = state; + } + + if (count > 0) + QuickSort(stateCache, 0, count - 1); + return count; + } + + + public static void CalcTime(ref Vector3 vel, ref AABB entityBB, ref AABB blockBB, + out float tx, out float ty, out float tz) { + float dx = vel.X > 0 ? blockBB.Min.X - entityBB.Max.X : entityBB.Min.X - blockBB.Max.X; + float dy = vel.Y > 0 ? blockBB.Min.Y - entityBB.Max.Y : entityBB.Min.Y - blockBB.Max.Y; + float dz = vel.Z > 0 ? blockBB.Min.Z - entityBB.Max.Z : entityBB.Min.Z - blockBB.Max.Z; + + tx = vel.X == 0 ? float.PositiveInfinity : Math.Abs(dx / vel.X); + ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs(dy / vel.Y); + tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs(dz / vel.Z); + + if (entityBB.Max.X >= blockBB.Min.X && entityBB.Min.X <= blockBB.Max.X) tx = 0; // Inlined XIntersects + if (entityBB.Max.Y >= blockBB.Min.Y && entityBB.Min.Y <= blockBB.Max.Y) ty = 0; // Inlined YIntersects + if (entityBB.Max.Z >= blockBB.Min.Z && entityBB.Min.Z <= blockBB.Max.Z) tz = 0; // Inlined ZIntersects + } + + static void QuickSort(State[] keys, int left, int right) { + while (left < right) { + int i = left, j = right; + float pivot = keys[(i + j) / 2].tSquared; + // partition the list + while (i <= j) { + while (pivot > keys[i].tSquared) i++; + while (pivot < keys[j].tSquared) j--; + + if (i <= j) { + State key = keys[i]; keys[i] = keys[j]; keys[j] = key; + i++; j--; + } + } + + // recurse into the smaller subset + if (j - left <= right - i) { + if (left < j) + QuickSort(keys, left, j); + left = i; + } else { + if (i < right) + QuickSort(keys, i, right); + right = j; + } + } + } + } } \ No newline at end of file diff --git a/src/Block.h b/src/Block.h index 81bcd5e14..c0b3afeac 100644 --- a/src/Block.h +++ b/src/Block.h @@ -1,9 +1,9 @@ #ifndef CC_BLOCK_H #define CC_BLOCK_H -#include "BlockID.h" #include "PackedCol.h" #include "Vectors.h" #include "Constants.h" +#include "BlockID.h" /* Stores properties and data for blocks. Also performs automatic rotation of directional blocks. Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 diff --git a/src/BlockID.h b/src/BlockID.h index 80063d3e2..1a836067c 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -1,5 +1,6 @@ #ifndef CC_BLOCKID_H #define CC_BLOCKID_H +#include "Core.h" /* TODO: Remove this include when we move to external defines */ /* List of all core/standard block IDs Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 */ @@ -84,8 +85,8 @@ enum BLOCKID { /* Number of blocks in original classic plus CPE blocks. */ BLOCK_CPE_COUNT = (BLOCK_MAX_CPE + 1), -#if USE16_BIT - BLOCK_MAX_DEFINED = 0x3FF, +#ifdef EXTENDED_BLOCKS + BLOCK_MAX_DEFINED = 0x2FF, #else BLOCK_MAX_DEFINED = 0xFF, #endif diff --git a/src/BlockPhysics.h b/src/BlockPhysics.h index 674dcd8d5..68f00dbaa 100644 --- a/src/BlockPhysics.h +++ b/src/BlockPhysics.h @@ -1,7 +1,6 @@ #ifndef CC_BLOCKPHYSICS_H #define CC_BLOCKPHYSICS_H #include "Core.h" -#include "BlockID.h" /* Implements simple block physics. Copyright 2014 - 2017 ClassicalSharp | Licensed under BSD-3 */ diff --git a/src/Builder.c b/src/Builder.c index 2f6ae93b6..3ea9331ec 100644 --- a/src/Builder.c +++ b/src/Builder.c @@ -231,39 +231,50 @@ static void Builder_Stretch(Int32 x1, Int32 y1, Int32 z1) { } } +#define Builder_ReadChunkBody(get_block)\ +for (yy = -1; yy < 17; ++yy) {\ + Int32 y = yy + y1;\ + if (y < 0) continue;\ + if (y >= World_Height) break;\ +\ + for (zz = -1; zz < 17; ++zz) {\ + Int32 z = zz + z1;\ + if (z < 0) continue;\ + if (z >= World_Length) break;\ +\ + /* need to subtract 1 as index is pre incremented in for loop. */ \ + Int32 index = World_Pack(x1 - 1, y, z) - 1;\ + Int32 chunkIndex = (yy + 1) * EXTCHUNK_SIZE_2 + (zz + 1) * EXTCHUNK_SIZE + (-1 + 1) - 1;\ +\ + for (xx = -1; xx < 17; ++xx) {\ + Int32 x = xx + x1;\ + ++index;\ + ++chunkIndex;\ +\ + if (x < 0) continue;\ + if (x >= World_Width) break;\ + BlockID block = get_block;\ +\ + allAir = allAir && Block_Draw[block] == DRAW_GAS;\ + allSolid = allSolid && Block_FullOpaque[block];\ + Builder_Chunk[chunkIndex] = block;\ + }\ + }\ +} + static void Builder_ReadChunkData(Int32 x1, Int32 y1, Int32 z1, bool* outAllAir, bool* outAllSolid) { bool allAir = true, allSolid = true; Int32 xx, yy, zz; - for (yy = -1; yy < 17; ++yy) { - Int32 y = yy + y1; - if (y < 0) continue; - if (y >= World_Height) break; - - for (zz = -1; zz < 17; ++zz) { - Int32 z = zz + z1; - if (z < 0) continue; - if (z >= World_Length) break; - - /* need to subtract 1 as index is pre incremented in for loop. */ - Int32 index = World_Pack(x1 - 1, y, z) - 1; - Int32 chunkIndex = (yy + 1) * EXTCHUNK_SIZE_2 + (zz + 1) * EXTCHUNK_SIZE + (-1 + 1) - 1; - - for (xx = -1; xx < 17; ++xx) { - Int32 x = xx + x1; - ++index; - ++chunkIndex; - - if (x < 0) continue; - if (x >= World_Width) break; - BlockID rawBlock = World_Blocks[index]; - - allAir = allAir && Block_Draw[rawBlock] == DRAW_GAS; - allSolid = allSolid && Block_FullOpaque[rawBlock]; - Builder_Chunk[chunkIndex] = rawBlock; - } - } +#ifndef EXTENDED_BLOCKS + Builder_ReadChunkBody(World_Blocks[index]); +#else + if (Block_UsedCount <= 256) { + Builder_ReadChunkBody(World_Blocks[index]); + } else { + Builder_ReadChunkBody(World_Blocks[index] | (World_Blocks2[index] << 8)); } +#endif *outAllAir = allAir; *outAllSolid = allSolid; diff --git a/src/Core.h b/src/Core.h index 662a4dd4b..a80f25c8a 100644 --- a/src/Core.h +++ b/src/Core.h @@ -42,7 +42,7 @@ typedef UInt8 bool; #define false 0 #define NULL ((void*)0) -//#define EXTENDED_BLOCKS +#define EXTENDED_BLOCKS #ifdef EXTENDED_BLOCKS typedef UInt16 BlockID; #else diff --git a/src/EntityComponents.c b/src/EntityComponents.c index b01f95a1d..5bde0b7af 100644 --- a/src/EntityComponents.c +++ b/src/EntityComponents.c @@ -804,8 +804,8 @@ static void Collisions_CollideWithReachableBlocks(struct CollisionsComp* comp, I for (i = 0; i < count; i++) { /* Unpack the block and coordinate data */ struct SearcherState state = Searcher_States[i]; - bPos.X = state.X >> 3; bPos.Y = state.Y >> 3; bPos.Z = state.Z >> 3; - Int32 block = (state.X & 0x7) | (state.Y & 0x7) << 3 | (state.Z & 0x7) << 6; + bPos.X = state.X >> 3; bPos.Y = state.Y >> 4; bPos.Z = state.Z >> 3; + Int32 block = (state.X & 0x7) | (state.Y & 0xF) << 3 | (state.Z & 0x7) << 7; Vector3_Add(&blockBB.Min, &Block_MinBB[block], &bPos); Vector3_Add(&blockBB.Max, &Block_MaxBB[block], &bPos); diff --git a/src/Lighting.c b/src/Lighting.c index f01baabb9..1e074f35f 100644 --- a/src/Lighting.c +++ b/src/Lighting.c @@ -246,50 +246,61 @@ static Int32 Lighting_InitialHeightmapCoverage(Int32 x1, Int32 z1, Int32 xCount, return elemsLeft; } +#define Lighting_CalculateBody(get_block)\ +for (y = World_Height - 1; y >= 0; y--) {\ + if (elemsLeft <= 0) { return true; } \ + Int32 mapIndex = World_Pack(x1, y, z1);\ + Int32 heightmapIndex = Lighting_Pack(x1, z1);\ +\ + for (z = 0; z < zCount; z++) {\ + Int32 baseIndex = mapIndex;\ + Int32 index = z * xCount;\ + for (x = 0; x < xCount;) {\ + Int32 curRunCount = skip[index];\ + x += curRunCount; mapIndex += curRunCount; index += curRunCount;\ +\ + if (x < xCount && Block_BlocksLight[get_block]) {\ + Int32 lightOffset = (Block_LightOffset[get_block] >> FACE_YMAX) & 1;\ + Lighting_Heightmap[heightmapIndex + x] = (Int16)(y - lightOffset);\ + elemsLeft--;\ + skip[index] = 0;\ + Int32 offset = prevRunCount + curRunCount;\ + Int32 newRunCount = skip[index - offset] + 1;\ +\ + /* consider case 1 0 1 0, where we are at 0 */ \ + /* we need to make this 3 0 0 0 and advance by 1 */ \ + Int32 oldRunCount = (x - offset + newRunCount) < xCount ? skip[index - offset + newRunCount] : 0; \ + if (oldRunCount != 0) {\ + skip[index - offset + newRunCount] = 0; \ + newRunCount += oldRunCount; \ + } \ + skip[index - offset] = newRunCount; \ + x += oldRunCount; index += oldRunCount; mapIndex += oldRunCount; \ + prevRunCount = newRunCount; \ + } else { \ + prevRunCount = 0; \ + }\ + x++; mapIndex++; index++; \ + }\ + prevRunCount = 0;\ + heightmapIndex += World_Width;\ + mapIndex = baseIndex + World_Width; /* advance one Z */ \ + }\ +} + static bool Lighting_CalculateHeightmapCoverage(Int32 x1, Int32 z1, Int32 xCount, Int32 zCount, Int32 elemsLeft, Int32* skip) { Int32 prevRunCount = 0; Int32 x, y, z; - for (y = World_Height - 1; y >= 0; y--) { - if (elemsLeft <= 0) return true; - Int32 mapIndex = World_Pack(x1, y, z1); - Int32 heightmapIndex = Lighting_Pack(x1, z1); - - for (z = 0; z < zCount; z++) { - Int32 baseIndex = mapIndex; - Int32 index = z * xCount; - for (x = 0; x < xCount;) { - Int32 curRunCount = skip[index]; - x += curRunCount; mapIndex += curRunCount; index += curRunCount; - - if (x < xCount && Block_BlocksLight[World_Blocks[mapIndex]]) { - Int32 lightOffset = (Block_LightOffset[World_Blocks[mapIndex]] >> FACE_YMAX) & 1; - Lighting_Heightmap[heightmapIndex + x] = (short)(y - lightOffset); - elemsLeft--; - skip[index] = 0; - Int32 offset = prevRunCount + curRunCount; - Int32 newRunCount = skip[index - offset] + 1; - - /* consider case 1 0 1 0, where we are at 0 */ - /* we need to make this 3 0 0 0 and advance by 1 */ - Int32 oldRunCount = (x - offset + newRunCount) < xCount ? skip[index - offset + newRunCount] : 0; - if (oldRunCount != 0) { - skip[index - offset + newRunCount] = 0; - newRunCount += oldRunCount; - } - skip[index - offset] = newRunCount; - x += oldRunCount; index += oldRunCount; mapIndex += oldRunCount; - prevRunCount = newRunCount; - } else { - prevRunCount = 0; - } - x++; mapIndex++; index++; - } - prevRunCount = 0; - heightmapIndex += World_Width; - mapIndex = baseIndex + World_Width; /* advance one Z */ - } +#ifndef EXTENDED_BLOCKS + Lighting_CalculateBody(World_Blocks[mapIndex]); +#else + if (Block_UsedCount <= 256) { + Lighting_CalculateBody(World_Blocks[mapIndex]); + } else { + Lighting_CalculateBody(World_Blocks[mapIndex] | (World_Blocks2[mapIndex] << 8)); } +#endif return false; } diff --git a/src/PacketHandlers.c b/src/PacketHandlers.c index f94f52513..7889e6996 100644 --- a/src/PacketHandlers.c +++ b/src/PacketHandlers.c @@ -26,11 +26,31 @@ #include "TerrainAtlas.h" /* Classic state */ -UInt8 classic_tabList[256 >> 3]; +UInt8 classic_tabList[ENTITIES_MAX_COUNT >> 3]; +struct Screen* classic_prevScreen; +bool classic_receivedFirstPos; + +/* Map state */ +bool map_begunLoading; +TimeMS map_receiveStart; +struct InflateState map_inflateState; +struct Stream map_stream, map_part; +struct GZipHeader map_gzHeader; +Int32 map_sizeIndex, map_index, map_volume; +UInt8 map_size[4]; +BlockRaw* map_blocks; + +#ifdef EXTENDED_BLOCKS +struct InflateState map2_inflateState; +struct Stream map2_stream; +Int32 map2_index; +BlockRaw* map2_blocks; +#endif /* CPE state */ Int32 cpe_serverExtensionsCount, cpe_pingTicks; Int32 cpe_envMapVer = 2, cpe_blockDefsExtVer = 2; +bool cpe_sendHeldBlock, cpe_useMessageTypes, cpe_extEntityPos, cpe_blockPerms, cpe_fastMap; bool cpe_twoWayPing, cpe_extTextures, cpe_extBlocks; /*########################################################################################################################* @@ -43,7 +63,7 @@ bool cpe_twoWayPing, cpe_extTextures, cpe_extBlocks; #else #define Handlers_ReadBlock(data, value)\ if (cpe_extBlocks) {\ - value = Stream_GetU16_BE(data, value); data += 2;\ + value = Stream_GetU16_BE(data); data += 2;\ } else { value = *data++; } #endif @@ -53,7 +73,7 @@ if (cpe_extBlocks) {\ #define Handlers_WriteBlock(data, value)\ if (cpe_extBlocks) {\ Stream_SetU16_BE(data, value); data += 2;\ -} else { *data++ = value; } +} else { *data++ = (BlockRaw)value; } #endif static void Handlers_ReadString(UInt8** ptr, String* str) { @@ -309,17 +329,6 @@ static void WoM_Tick(void) { /*########################################################################################################################* *----------------------------------------------------Classic protocol-----------------------------------------------------* *#########################################################################################################################*/ -TimeMS mapReceiveStart; -struct InflateState mapInflateState; -struct Stream mapInflateStream; -bool mapInflateInited; -struct GZipHeader gzHeader; -Int32 mapSizeIndex, mapIndex, mapVolume; -UInt8 mapSize[4]; -BlockRaw* map; -struct Stream mapPartStream; -struct Screen* prevScreen; -bool receivedFirstPosition; void Classic_WriteChat(const String* text, bool partial) { UInt8* data = ServerConnection_WriteBuffer; @@ -335,7 +344,8 @@ void Classic_WritePosition(Vector3 pos, float rotY, float headX) { UInt8* data = ServerConnection_WriteBuffer; *data++ = OPCODE_ENTITY_TELEPORT; { - *data++ = cpe_sendHeldBlock ? Inventory_SelectedBlock : ENTITIES_SELF_ID; /* TODO: extended blocks */ + BlockID payload = cpe_sendHeldBlock ? Inventory_SelectedBlock : ENTITIES_SELF_ID; + Handlers_WriteBlock(data, payload); Int32 x = (Int32)(pos.X * 32); Int32 y = (Int32)(pos.Y * 32) + 51; Int32 z = (Int32)(pos.Z * 32); @@ -364,7 +374,7 @@ void Classic_WriteSetBlock(Int32 x, Int32 y, Int32 z, bool place, BlockID block) Stream_SetU16_BE(data, y); data += 2; Stream_SetU16_BE(data, z); data += 2; *data++ = place; - *data++ = block; /* TODO: extended blocks */ + Handlers_WriteBlock(data, block); } ServerConnection_WriteBuffer = data; } @@ -403,102 +413,127 @@ static void Classic_Ping(UInt8* data) { } static void Classic_StartLoading(void) { World_Reset(); Event_RaiseVoid(&WorldEvents_NewMap); - Stream_ReadonlyMemory(&mapPartStream, NULL, 0); + Stream_ReadonlyMemory(&map_part, NULL, 0); - prevScreen = Gui_Active; - if (prevScreen == LoadingScreen_UNSAFE_RawPointer) { + classic_prevScreen = Gui_Active; + if (classic_prevScreen == LoadingScreen_UNSAFE_RawPointer) { /* otherwise replacing LoadingScreen with LoadingScreen will cause issues */ Gui_FreeActive(); - prevScreen = NULL; + classic_prevScreen = NULL; } Gui_SetActive(LoadingScreen_MakeInstance(&ServerConnection_ServerName, &ServerConnection_ServerMOTD)); WoM_CheckMotd(); - receivedFirstPosition = false; - GZipHeader_Init(&gzHeader); + classic_receivedFirstPos = false; - Inflate_MakeStream(&mapInflateStream, &mapInflateState, &mapPartStream); - mapInflateInited = true; + GZipHeader_Init(&map_gzHeader); + Inflate_MakeStream(&map_stream, &map_inflateState, &map_part); + map_begunLoading = true; - mapSizeIndex = 0; - mapIndex = 0; - mapReceiveStart = DateTime_CurrentUTC_MS(); + map_sizeIndex = 0; + map_index = 0; + map_receiveStart = DateTime_CurrentUTC_MS(); + +#ifdef EXTENDED_BLOCKS + Inflate_MakeStream(&map2_stream, &map2_inflateState, &map_part); + map2_index = 0; +#endif } static void Classic_LevelInit(UInt8* data) { - if (!mapInflateInited) Classic_StartLoading(); + if (!map_begunLoading) Classic_StartLoading(); /* Fast map puts volume in header, doesn't bother with gzip */ if (cpe_fastMap) { - mapVolume = Stream_GetU32_BE(data); - gzHeader.Done = true; - mapSizeIndex = sizeof(UInt32); - map = Mem_Alloc(mapVolume, sizeof(BlockID), "map blocks"); + map_volume = Stream_GetU32_BE(data); + map_gzHeader.Done = true; + map_sizeIndex = sizeof(UInt32); + map_blocks = Mem_Alloc(map_volume, 1, "map blocks"); } } static void Classic_LevelDataChunk(UInt8* data) { /* Workaround for some servers that send LevelDataChunk before LevelInit due to their async sending behaviour */ - if (!mapInflateInited) Classic_StartLoading(); + if (!map_begunLoading) Classic_StartLoading(); Int32 usedLength = Stream_GetU16_BE(data); data += 2; - mapPartStream.Meta.Mem.Cur = data; - mapPartStream.Meta.Mem.Base = data; - mapPartStream.Meta.Mem.Left = usedLength; - mapPartStream.Meta.Mem.Length = usedLength; + map_part.Meta.Mem.Cur = data; + map_part.Meta.Mem.Base = data; + map_part.Meta.Mem.Left = usedLength; + map_part.Meta.Mem.Length = usedLength; data += 1024; UInt8 value = *data; /* progress in original classic, but we ignore it */ - if (!gzHeader.Done) { - ReturnCode res = GZipHeader_Read(&mapPartStream, &gzHeader); + if (!map_gzHeader.Done) { + ReturnCode res = GZipHeader_Read(&map_part, &map_gzHeader); if (res && res != ERR_END_OF_STREAM) ErrorHandler_Fail2(res, "reading map data"); } - if (gzHeader.Done) { - if (mapSizeIndex < sizeof(UInt32)) { - UInt8* src = mapSize + mapSizeIndex; - UInt32 count = sizeof(UInt32) - mapSizeIndex, modified = 0; - mapInflateStream.Read(&mapInflateStream, src, count, &modified); - mapSizeIndex += modified; + if (map_gzHeader.Done) { + if (map_sizeIndex < 4) { + UInt32 left = 4 - map_sizeIndex, read = 0; + map_stream.Read(&map_stream, &map_size[map_sizeIndex], left, &read); map_sizeIndex += read; } - if (mapSizeIndex == sizeof(UInt32)) { - if (!map) { - mapVolume = Stream_GetU32_BE(mapSize); - map = Mem_Alloc(mapVolume, sizeof(BlockID), "map blocks"); + if (map_sizeIndex == 4) { + if (!map_blocks) { + map_volume = Stream_GetU32_BE(map_size); + map_blocks = Mem_Alloc(map_volume, 1, "map blocks"); } - UInt8* src = map + mapIndex; - UInt32 count = mapVolume - mapIndex, modified = 0; - mapInflateStream.Read(&mapInflateStream, src, count, &modified); - mapIndex += modified; +#ifndef EXTENDED_BLOCKS + UInt32 left = map_volume - map_index, read = 0; + map_stream.Read(&map_stream, &map_blocks[map_index], left, &read); + map_index += read; +#else + if (cpe_extBlocks && value) { + /* Only allocate map2 when needed */ + if (!map2_blocks) map2_blocks = Mem_Alloc(map_volume, 1, "map blocks upper"); + UInt32 left = map_volume - map2_index, read = 0; + map2_stream.Read(&map2_stream, &map2_blocks[map2_index], left, &read); map2_index += read; + } else { + UInt32 left = map_volume - map_index, read = 0; + map_stream.Read(&map_stream, &map_blocks[map_index], left, &read); map_index += read; + } +#endif } } - float progress = !map ? 0.0f : (float)mapIndex / mapVolume; + float progress = !map_blocks ? 0.0f : (float)map_index / map_volume; Event_RaiseFloat(&WorldEvents_Loading, progress); } static void Classic_LevelFinalise(UInt8* data) { Gui_CloseActive(); - Gui_Active = prevScreen; - prevScreen = NULL; + Gui_Active = classic_prevScreen; + classic_prevScreen = NULL; Gui_CalcCursorVisible(); - Int32 mapWidth = Stream_GetU16_BE(&data[0]); - Int32 mapHeight = Stream_GetU16_BE(&data[2]); - Int32 mapLength = Stream_GetU16_BE(&data[4]); + Int32 width = Stream_GetU16_BE(&data[0]); + Int32 height = Stream_GetU16_BE(&data[2]); + Int32 length = Stream_GetU16_BE(&data[4]); - Int32 loadingMs = (Int32)(DateTime_CurrentUTC_MS() - mapReceiveStart); + Int32 loadingMs = (Int32)(DateTime_CurrentUTC_MS() - map_receiveStart); Platform_Log1("map loading took: %i", &loadingMs); - World_SetNewMap(map, mapVolume, mapWidth, mapHeight, mapLength); + World_SetNewMap(map_blocks, map_volume, width, height, length); +#ifdef EXTENDED_BLOCKS + if (cpe_extBlocks) { + /* defer allocation of scond map array if possible */ + World_Blocks2 = map2_blocks ? map2_blocks : map_blocks; + Block_SetUsedCount(map2_blocks ? 768 : 256); + } +#endif + Event_RaiseVoid(&WorldEvents_MapLoaded); WoM_CheckSendWomID(); - map = NULL; - mapInflateInited = false; + map_blocks = NULL; + map_begunLoading = false; +#ifdef EXTENDED_BLOCKS + map2_blocks = NULL; +#endif } static void Classic_SetBlock(UInt8* data) { @@ -632,14 +667,14 @@ static void Classic_ReadAbsoluteLocation(UInt8* data, EntityID id, bool interpol float rotY = Math_Packed2Deg(*data++); float headX = Math_Packed2Deg(*data++); - if (id == ENTITIES_SELF_ID) receivedFirstPosition = true; + if (id == ENTITIES_SELF_ID) classic_receivedFirstPos = true; struct LocationUpdate update; LocationUpdate_MakePosAndOri(&update, pos, rotY, headX, false); Handlers_UpdateLocation(id, &update, interpolate); } static void Classic_Reset(void) { - mapInflateInited = false; - receivedFirstPosition = false; + map_begunLoading = false; + classic_receivedFirstPos = false; Net_Set(OPCODE_HANDSHAKE, Classic_Handshake, 131); Net_Set(OPCODE_PING, Classic_Ping, 1); @@ -661,22 +696,22 @@ static void Classic_Reset(void) { } static void Classic_Tick(void) { - if (!receivedFirstPosition) return; - struct Entity* entity = &LocalPlayer_Instance.Base; - Classic_WritePosition(entity->Position, entity->HeadY, entity->HeadX); + if (!classic_receivedFirstPos) return; + struct Entity* e = &LocalPlayer_Instance.Base; + Classic_WritePosition(e->Position, e->HeadY, e->HeadX); } /*########################################################################################################################* *------------------------------------------------------CPE protocol-------------------------------------------------------* *#########################################################################################################################*/ -const char* cpe_clientExtensions[29] = { +const char* cpe_clientExtensions[30] = { "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", - "ExtendedTextures", + "ExtendedTextures", "ExtendedBlocks", }; static void CPE_SetMapEnvUrl(UInt8* data); @@ -756,8 +791,16 @@ static void CPE_SendCpeExtInfoReply(void) { #ifndef EXTENDED_TEXTURES count--; #endif +#ifndef EXTENDED_BLOCKS + count--; +#endif +#ifdef EXTENDED_BLOCKS + if (!Game_AllowCustomBlocks) count -= 3; +#else if (!Game_AllowCustomBlocks) count -= 2; +#endif + CPE_WriteExtInfo(&ServerConnection_AppName, count); Net_SendPacket(); Int32 i, ver; @@ -772,11 +815,17 @@ static void CPE_SendCpeExtInfoReply(void) { if (!Game_AllowCustomBlocks) { if (String_CaselessEqualsConst(&name, "BlockDefinitionsExt")) continue; if (String_CaselessEqualsConst(&name, "BlockDefinitions")) continue; +#ifdef EXTENDED_BLOCKS + 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_WriteExtEntry(&name, ver); Net_SendPacket(); @@ -851,6 +900,21 @@ static void CPE_ExtEntry(UInt8* data) { cpe_extTextures = true; } #endif +#ifdef EXTENDED_BLOCKS + else if (String_CaselessEqualsConst(&ext, "ExtendedBlocks")) { + if (!Game_AllowCustomBlocks) return; + cpe_extBlocks = true; + + Net_PacketSizes[OPCODE_SET_BLOCK] += 1; + Net_PacketSizes[OPCODE_HOLD_THIS] += 1; + Net_PacketSizes[OPCODE_SET_BLOCK_PERMISSION] += 1; + Net_PacketSizes[OPCODE_DEFINE_BLOCK] += 1; + Net_PacketSizes[OPCODE_UNDEFINE_BLOCK] += 1; + Net_PacketSizes[OPCODE_DEFINE_BLOCK_EXT] += 1; + Net_PacketSizes[OPCODE_SET_INVENTORY_ORDER] += 2; + Net_PacketSizes[OPCODE_BULK_BLOCK_UPDATE] += 256 / 4; + } +#endif } static void CPE_SetClickDistance(UInt8* data) { @@ -1067,6 +1131,23 @@ static void CPE_BulkBlockUpdate(UInt8* data) { } data += (BULK_MAX_BLOCKS - count) * sizeof(Int32); + BlockID blocks[BULK_MAX_BLOCKS]; + for (i = 0; i < count; i++) { + blocks[i] = data[i]; + } + data += BULK_MAX_BLOCKS; + + if (cpe_extBlocks) { + for (i = 0; i < count; i += 4) { + UInt8 flags = data[i >> 2]; + blocks[i + 0] |= (BlockID)((flags & 0x03) << 8); + blocks[i + 1] |= (BlockID)((flags & 0x0C) << 6); + blocks[i + 2] |= (BlockID)((flags & 0x30) << 4); + blocks[i + 3] |= (BlockID)((flags & 0xC0) << 2); + } + data += BULK_MAX_BLOCKS / 4; + } + Int32 x, y, z; for (i = 0; i < count; i++) { Int32 index = indices[i]; @@ -1074,7 +1155,7 @@ static void CPE_BulkBlockUpdate(UInt8* data) { World_Unpack(index, x, y, z); if (World_IsValidPos(x, y, z)) { - Game_UpdateBlock(x, y, z, data[i]); + Game_UpdateBlock(x, y, z, blocks[i]); } } } @@ -1207,7 +1288,7 @@ static void CPE_Reset(void) { cpe_sendHeldBlock = false; cpe_useMessageTypes = false; cpe_envMapVer = 2; cpe_blockDefsExtVer = 2; cpe_needD3Fix = false; cpe_extEntityPos = false; cpe_twoWayPing = false; - cpe_extTextures = false; cpe_fastMap = false; + cpe_extTextures = false; cpe_fastMap = false; cpe_extBlocks = false; Game_UseCPEBlocks = false; if (!Game_UseCPE) return; diff --git a/src/PacketHandlers.h b/src/PacketHandlers.h index eebf8de08..b3ebbf7f2 100644 --- a/src/PacketHandlers.h +++ b/src/PacketHandlers.h @@ -13,7 +13,7 @@ void Handlers_RemoveEntity(EntityID id); void Handlers_Reset(void); void Handlers_Tick(void); -bool cpe_sendHeldBlock, cpe_useMessageTypes, cpe_needD3Fix, cpe_extEntityPos, cpe_blockPerms, cpe_fastMap; +bool cpe_needD3Fix; void Classic_WriteChat(const String* text, bool partial); void Classic_WritePosition(Vector3 pos, float rotY, float headX); void Classic_WriteSetBlock(Int32 x, Int32 y, Int32 z, bool place, BlockID block); diff --git a/src/Physics.c b/src/Physics.c index 368e238ec..8994fa3ff 100644 --- a/src/Physics.c +++ b/src/Physics.c @@ -185,9 +185,9 @@ Int32 Searcher_FindReachableBlocks(struct Entity* entity, struct AABB* entityBB, Searcher_CalcTime(&vel, entityBB, &blockBB, &tx, &ty, &tz); if (tx > 1.0f || ty > 1.0f || tz > 1.0f) continue; - curState->X = (x << 3) | (block & 0x07); - curState->Y = (y << 3) | ((block & 0x38) >> 3); - curState->Z = (z << 3) | ((block & 0xC0) >> 6); + curState->X = (x << 3) | (block & 0x007); + curState->Y = (y << 4) | ((block & 0x078) >> 3); + curState->Z = (z << 3) | ((block & 0x380) >> 7); curState->tSquared = tx * tx + ty * ty + tz * tz; curState++; } diff --git a/src/Widgets.c b/src/Widgets.c index 0e973e4e0..e11ca12c4 100644 --- a/src/Widgets.c +++ b/src/Widgets.c @@ -529,7 +529,8 @@ static void TableWidget_MakeBlockDesc(String* desc, BlockID block) { String_AppendString(desc, &name); if (Game_ClassicMode) return; - String_Format1(desc, " (ID %b&f", &block); + Int32 block_ = block; + String_Format1(desc, " (ID %i&f", &block_); if (!Block_CanPlace[block]) { String_AppendConst(desc, ", place &cNo&f"); } if (!Block_CanDelete[block]) { String_AppendConst(desc, ", delete &cNo&f"); } String_Append(desc, ')');