Skip to content

Commit bc5c66e

Browse files
authored
add surface indicator byte (#102)
1 parent c5bd88c commit bc5c66e

File tree

4 files changed

+68
-22
lines changed

4 files changed

+68
-22
lines changed

packages/world/src/systems/libraries/TerrainLib.sol

+26
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ObjectTypes } from "../../ObjectTypes.sol";
1111

1212
uint256 constant VERSION_PADDING = 1;
1313
uint256 constant BIOME_PADDING = 1;
14+
uint256 constant SURFACE_PADDING = 1;
1415

1516
library TerrainLib {
1617
using SSTORE2 for address;
@@ -73,6 +74,30 @@ library TerrainLib {
7374
return uint8(biome);
7475
}
7576

77+
/// @notice Returns true if the chunk is the highest non-air chunk in this X/Z column
78+
/// @dev Assumes to be called from a root system.
79+
function _isSurfaceChunk(Vec3 chunkCoord) internal view returns (bool) {
80+
return isSurfaceChunk(chunkCoord, address(this));
81+
}
82+
83+
/// @notice Returns true if the chunk is the highest non-air chunk in this X/Z column
84+
/// @dev Can be called from either a root or non-root system, but consumes slightly more gas.
85+
function isSurfaceChunk(Vec3 chunkCoord) internal view returns (bool) {
86+
return isSurfaceChunk(chunkCoord, WorldContextConsumerLib._world());
87+
}
88+
89+
/// @notice Returns true if the chunk is the highest non-air chunk in this X/Z column
90+
function isSurfaceChunk(Vec3 chunkCoord, address world) internal view returns (bool) {
91+
require(_isChunkExplored(chunkCoord, world), "Chunk not explored");
92+
93+
address chunkPointer = _getChunkPointer(chunkCoord, world);
94+
bytes1 version = chunkPointer.readBytes1(0);
95+
require(version == _VERSION, "Unsupported chunk encoding version");
96+
97+
bytes1 isSurface = chunkPointer.readBytes1(2);
98+
return uint8(isSurface) == 1;
99+
}
100+
76101
/// @dev Get the relative coordinate of a voxel coordinate within a chunk
77102
function _getRelativeCoord(Vec3 coord) internal pure returns (Vec3) {
78103
return
@@ -89,6 +114,7 @@ library TerrainLib {
89114
return
90115
VERSION_PADDING +
91116
BIOME_PADDING +
117+
SURFACE_PADDING +
92118
uint256(
93119
int256(relativeCoord.x()) * CHUNK_SIZE ** 2 + int256(relativeCoord.y()) * CHUNK_SIZE + int256(relativeCoord.z())
94120
);

packages/world/test/BiomesTest.sol

+6-3
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ abstract contract BiomesTest is MudTest, GasReporter, BiomesAssertions {
121121
function setupFlatChunk(Vec3 coord) internal {
122122
uint8[][][] memory chunk = _getFlatChunk();
123123
uint8 biome = 1;
124-
bytes memory encodedChunk = encodeChunk(biome, chunk);
124+
bool isSurface = true;
125+
bytes memory encodedChunk = encodeChunk(biome, isSurface, chunk);
125126
Vec3 chunkCoord = coord.toChunkCoord();
126127
bytes32[] memory merkleProof = new bytes32[](0);
127128

@@ -170,7 +171,8 @@ abstract contract BiomesTest is MudTest, GasReporter, BiomesAssertions {
170171
function setupAirChunk(Vec3 coord) internal {
171172
uint8[][][] memory chunk = _getAirChunk();
172173
uint8 biome = 1;
173-
bytes memory encodedChunk = encodeChunk(biome, chunk);
174+
bool isSurface = true;
175+
bytes memory encodedChunk = encodeChunk(biome, isSurface, chunk);
174176
Vec3 chunkCoord = coord.toChunkCoord();
175177
bytes32[] memory merkleProof = new bytes32[](0);
176178

@@ -187,7 +189,8 @@ abstract contract BiomesTest is MudTest, GasReporter, BiomesAssertions {
187189
function setupWaterChunk(Vec3 coord) internal {
188190
uint8[][][] memory chunk = _getWaterChunk();
189191
uint8 biome = 2;
190-
bytes memory encodedChunk = encodeChunk(biome, chunk);
192+
bool isSurface = true;
193+
bytes memory encodedChunk = encodeChunk(biome, isSurface, chunk);
191194
Vec3 chunkCoord = coord.toChunkCoord();
192195
bytes32[] memory merkleProof = new bytes32[](0);
193196

packages/world/test/Terrain.t.sol

+24-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pragma solidity >=0.8.24;
33

44
import { BiomesTest } from "./BiomesTest.sol";
55

6-
import { TerrainLib, VERSION_PADDING, BIOME_PADDING } from "../src/systems/libraries/TerrainLib.sol";
6+
import { TerrainLib, VERSION_PADDING, BIOME_PADDING, SURFACE_PADDING } from "../src/systems/libraries/TerrainLib.sol";
77
import { ObjectTypeId } from "../src/ObjectTypeId.sol";
88
import { ObjectTypes } from "../src/ObjectTypes.sol";
99
import { Vec3, vec3 } from "../src/Vec3.sol";
@@ -51,19 +51,19 @@ contract TerrainTest is BiomesTest {
5151
function testGetBlockIndex() public {
5252
Vec3 coord = vec3(1, 2, 2);
5353
uint256 index = TerrainLib._getBlockIndex(coord);
54-
assertEq(index, 1 * 256 + 2 * 16 + 2 + VERSION_PADDING + BIOME_PADDING);
54+
assertEq(index, 1 * 256 + 2 * 16 + 2 + VERSION_PADDING + BIOME_PADDING + SURFACE_PADDING);
5555

5656
coord = vec3(16, 17, 18);
5757
index = TerrainLib._getBlockIndex(coord);
58-
assertEq(index, 0 * 256 + 1 * 16 + 2 + VERSION_PADDING + BIOME_PADDING);
58+
assertEq(index, 0 * 256 + 1 * 16 + 2 + VERSION_PADDING + BIOME_PADDING + SURFACE_PADDING);
5959

6060
coord = vec3(-1, -2, -3);
6161
index = TerrainLib._getBlockIndex(coord);
62-
assertEq(index, 15 * 256 + 14 * 16 + 13 + VERSION_PADDING + BIOME_PADDING);
62+
assertEq(index, 15 * 256 + 14 * 16 + 13 + VERSION_PADDING + BIOME_PADDING + SURFACE_PADDING);
6363

6464
coord = vec3(16, -17, -18);
6565
index = TerrainLib._getBlockIndex(coord);
66-
assertEq(index, 0 * 256 + 15 * 16 + 14 + VERSION_PADDING + BIOME_PADDING);
66+
assertEq(index, 0 * 256 + 15 * 16 + 14 + VERSION_PADDING + BIOME_PADDING + SURFACE_PADDING);
6767
}
6868

6969
function testGetChunkSalt() public {
@@ -78,7 +78,7 @@ contract TerrainTest is BiomesTest {
7878
assertEq(salt, bytes32(uint256(uint96(bytes12(abi.encodePacked(chunkCoord))))));
7979
}
8080

81-
function _getTestChunk() internal pure returns (uint8[][][] memory chunk) {
81+
function _getTestChunk() internal pure returns (uint8[][][] memory chunk, uint8 biome, bool isSurface) {
8282
chunk = new uint8[][][](uint256(int256(CHUNK_SIZE)));
8383
for (uint256 x = 0; x < uint256(int256(CHUNK_SIZE)); x++) {
8484
chunk[x] = new uint8[][](uint256(int256(CHUNK_SIZE)));
@@ -89,12 +89,13 @@ contract TerrainTest is BiomesTest {
8989
}
9090
}
9191
}
92+
biome = 1;
93+
isSurface = true;
9294
}
9395

9496
function testExploreChunk() public {
95-
uint8[][][] memory chunk = _getTestChunk();
96-
uint8 inputBiome = 1;
97-
bytes memory encodedChunk = encodeChunk(inputBiome, chunk);
97+
(uint8[][][] memory chunk, uint8 inputBiome, bool inputIsSurface) = _getTestChunk();
98+
bytes memory encodedChunk = encodeChunk(inputBiome, inputIsSurface, chunk);
9899
Vec3 chunkCoord = vec3(0, 0, 0);
99100
bytes32[] memory merkleProof = new bytes32[](0);
100101

@@ -123,6 +124,16 @@ contract TerrainTest is BiomesTest {
123124

124125
assertEq(biome, inputBiome);
125126

127+
startGasReport("TerrainLib.isSurfaceChunk (non-root)");
128+
bool isSurface = TerrainLib.isSurfaceChunk(coord.toChunkCoord());
129+
endGasReport();
130+
131+
startGasReport("TerrainLib.isSurfaceChunk (root)");
132+
isSurface = TerrainLib.isSurfaceChunk(coord.toChunkCoord(), worldAddress);
133+
endGasReport();
134+
135+
assertEq(isSurface, inputIsSurface);
136+
126137
for (int32 x = 0; x < CHUNK_SIZE; x++) {
127138
for (int32 y = 0; y < CHUNK_SIZE; y++) {
128139
for (int32 z = 0; z < CHUNK_SIZE; z++) {
@@ -138,9 +149,8 @@ contract TerrainTest is BiomesTest {
138149
}
139150

140151
function testExploreChunk_Fail_ChunkAlreadyExplored() public {
141-
uint8[][][] memory chunk = _getTestChunk();
142-
uint8 biome = 1;
143-
bytes memory encodedChunk = encodeChunk(biome, chunk);
152+
(uint8[][][] memory chunk, uint8 biome, bool isSurface) = _getTestChunk();
153+
bytes memory encodedChunk = encodeChunk(biome, isSurface, chunk);
144154
Vec3 chunkCoord = vec3(0, 0, 0);
145155
IWorld(worldAddress).exploreChunk(chunkCoord, encodedChunk, new bytes32[](0));
146156

@@ -149,9 +159,8 @@ contract TerrainTest is BiomesTest {
149159
}
150160

151161
function testGetBlockType() public {
152-
uint8[][][] memory chunk = _getTestChunk();
153-
uint8 biome = 1;
154-
bytes memory encodedChunk = encodeChunk(biome, chunk);
162+
(uint8[][][] memory chunk, uint8 biome, bool isSurface) = _getTestChunk();
163+
bytes memory encodedChunk = encodeChunk(biome, isSurface, chunk);
155164

156165
// Test we can get the block type for a voxel in the chunk
157166
Vec3 chunkCoord = vec3(0, 0, 0);

packages/world/test/utils/encodeChunk.sol

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,26 @@
22
pragma solidity >=0.8.24;
33

44
import { CHUNK_SIZE } from "../../src/Constants.sol";
5-
import { VERSION_PADDING, BIOME_PADDING } from "../../src/systems/libraries/TerrainLib.sol";
5+
import { VERSION_PADDING, BIOME_PADDING, SURFACE_PADDING } from "../../src/systems/libraries/TerrainLib.sol";
66

7-
function encodeChunk(uint8 biome, uint8[][][] memory chunk) pure returns (bytes memory encodedChunk) {
7+
function encodeChunk(uint8 biome, bool isSurface, uint8[][][] memory chunk) pure returns (bytes memory encodedChunk) {
88
bytes1 version = bytes1(uint8(0));
9-
encodedChunk = new bytes(uint256(int256(CHUNK_SIZE)) ** 3 + VERSION_PADDING + BIOME_PADDING);
9+
encodedChunk = new bytes(uint256(int256(CHUNK_SIZE)) ** 3 + VERSION_PADDING + BIOME_PADDING + SURFACE_PADDING);
1010
encodedChunk[0] = version;
1111
encodedChunk[1] = bytes1(biome);
12+
encodedChunk[2] = bytes1(isSurface ? 1 : 0);
1213
for (uint256 x = 0; x < uint256(int256(CHUNK_SIZE)); x++) {
1314
for (uint256 y = 0; y < uint256(int256(CHUNK_SIZE)); y++) {
1415
for (uint256 z = 0; z < uint256(int256(CHUNK_SIZE)); z++) {
1516
encodedChunk[
16-
VERSION_PADDING + BIOME_PADDING + x * uint256(int256(CHUNK_SIZE)) ** 2 + y * uint256(int256(CHUNK_SIZE)) + z
17+
VERSION_PADDING +
18+
BIOME_PADDING +
19+
SURFACE_PADDING +
20+
x *
21+
uint256(int256(CHUNK_SIZE)) ** 2 +
22+
y *
23+
uint256(int256(CHUNK_SIZE)) +
24+
z
1725
] = bytes1(uint8(chunk[x][y][z]));
1826
}
1927
}

0 commit comments

Comments
 (0)