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, ')');