Skip to content

Commit a46b932

Browse files
authored
Merge pull request #31 from rozukke/feature/chunk-data-structure
Add Chunk datastructure for more convenient access to `getBlocks`
2 parents 6ed3d39 + e795716 commit a46b932

File tree

6 files changed

+138
-71
lines changed

6 files changed

+138
-71
lines changed

include/mcpp/mcpp.h

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,6 @@ class MinecraftConnection {
2525
/// Handle to the socket connection.
2626
std::unique_ptr<SocketConnection> conn;
2727

28-
/**
29-
* @brief Helper function to convert flat block array to 3D.
30-
*
31-
* @param loc1 The first coordinate.
32-
* @param loc2 The second coordinate.
33-
* @param inVector The input flat block array.
34-
* @return A 3D vector representing the blocks.
35-
*/
36-
static std::vector<std::vector<std::vector<BlockType>>>
37-
unflattenBlocksArray(const Coordinate& loc1, const Coordinate& loc2,
38-
const std::vector<BlockType>& inVector);
39-
4028
public:
4129
/**
4230
* @brief Represents the main endpoint for interaction with the minecraft
@@ -132,10 +120,9 @@ class MinecraftConnection {
132120
*
133121
* @param loc1 1st corner of the cuboid
134122
* @param loc2 2nd corner of the cuboid
135-
* @return 3D vector of BlockType in the specified cuboid.
123+
* @return Chunk containing the blocks in the specified area.
136124
*/
137-
std::vector<std::vector<std::vector<BlockType>>>
138-
getBlocks(const Coordinate& loc1, const Coordinate& loc2);
125+
Chunk getBlocks(const Coordinate& loc1, const Coordinate& loc2);
139126

140127
/**
141128
* @brief Returns the height of the specific provided x and y coordinate

include/mcpp/util.h

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "block.h"
34
#include <ostream>
45
#include <vector>
56

@@ -12,8 +13,7 @@ namespace mcpp {
1213
* Represented using integers since sub-unit coordinates are not of particular
1314
* relevance. Allows for operations such as addition between coordinates.
1415
*/
15-
class Coordinate {
16-
public:
16+
struct Coordinate {
1717
/**
1818
* @brief Constructs a Coordinate object with integer values.
1919
*
@@ -87,6 +87,46 @@ class Coordinate {
8787
int z;
8888
};
8989

90+
/**
91+
* Stores a 3D cuboid of BlockTypes while preserving their relative location to
92+
* the base point they were gathered at and each other.
93+
*/
94+
struct Chunk {
95+
/**
96+
* Initialized by copying from a flat vector of blocks
97+
*/
98+
Chunk(const Coordinate& loc1, const Coordinate& loc2,
99+
const std::vector<BlockType>& block_list);
100+
101+
~Chunk();
102+
103+
/**
104+
* Accesses the Minecraft block at absolute position pos and returns its
105+
* BlockType if it is in the included area.
106+
* @param pos: Abolute position in the Minecraft world to query BlockType
107+
* for
108+
* @return BlockType at specified location
109+
*/
110+
BlockType get_worldspace(const Coordinate& pos);
111+
112+
/**
113+
* Local equivalent of get_worldspace, equivalent to a 3D array access of
114+
* the internal data.
115+
* @param x: x element of array access
116+
* @param y: y element of array access
117+
* @param z: z element of array access
118+
* @return BlockType at specified location
119+
*/
120+
BlockType get(int x, int y, int z);
121+
122+
private:
123+
Coordinate base_pt;
124+
BlockType* raw_data;
125+
int y_len;
126+
int x_len;
127+
int z_len;
128+
};
129+
90130
/**
91131
* Represents a 2D area of the world with the y coordinates of the highest
92132
* non-air blocks at each (x,z)
@@ -125,4 +165,4 @@ struct HeightMap {
125165
int* raw_heights;
126166
};
127167

128-
} // namespace mcpp
168+
} // namespace mcpp

src/mcpp.cpp

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ BlockType MinecraftConnection::getBlock(const Coordinate& loc) {
8181
return {parsedInts[0], parsedInts[1]};
8282
}
8383

84-
std::vector<std::vector<std::vector<BlockType>>>
85-
MinecraftConnection::getBlocks(const Coordinate& loc1, const Coordinate& loc2) {
84+
Chunk MinecraftConnection::getBlocks(const Coordinate& loc1,
85+
const Coordinate& loc2) {
8686
std::string returnValue =
8787
conn->sendReceiveCommand("world.getBlocksWithData", loc1.x, loc1.y,
8888
loc1.z, loc2.x, loc2.y, loc2.z);
@@ -108,7 +108,8 @@ MinecraftConnection::getBlocks(const Coordinate& loc1, const Coordinate& loc2) {
108108
break;
109109
}
110110
}
111-
return unflattenBlocksArray(loc1, loc2, result);
111+
112+
return Chunk{loc1, loc2, result};
112113
}
113114

114115
int MinecraftConnection::getHeight(int x, int z) {
@@ -128,29 +129,4 @@ const HeightMap MinecraftConnection::getHeights(const Coordinate& loc1,
128129
return HeightMap(loc1, loc2, returnVector);
129130
}
130131

131-
std::vector<std::vector<std::vector<BlockType>>>
132-
MinecraftConnection::unflattenBlocksArray(
133-
const Coordinate& loc1, const Coordinate& loc2,
134-
const std::vector<BlockType>& inVector) {
135-
// Initialise empty vector of correct size and shape
136-
int y_len = abs(loc2.y - loc1.y) + 1;
137-
int x_len = abs(loc2.x - loc1.x) + 1;
138-
int z_len = abs(loc2.z - loc1.z) + 1;
139-
std::vector<std::vector<std::vector<BlockType>>> returnVector(
140-
y_len, std::vector<std::vector<BlockType>>(
141-
x_len, std::vector<BlockType>(z_len, BlockType(0))));
142-
143-
int index = 0;
144-
for (int y = 0; y < y_len; y++) {
145-
for (int x = 0; x < x_len; x++) {
146-
for (int z = 0; z < z_len; z++) {
147-
returnVector[y][x][z] = inVector[index];
148-
index++;
149-
}
150-
}
151-
}
152-
153-
return returnVector;
154-
}
155-
156132
} // namespace mcpp

src/util.cpp

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include "../include/mcpp/util.h"
2-
#include <cstdlib>
32
#include <stdexcept>
43
#include <string>
4+
#include <vector>
55

66
namespace mcpp {
77

@@ -45,11 +45,55 @@ Coordinate Coordinate::clone() const {
4545
return Coordinate(this->x, this->y, this->z);
4646
}
4747

48+
std::string to_string(const Coordinate& coord) {
49+
using std::to_string;
50+
return "(" + to_string(coord.x) + "," + to_string(coord.y) + "," +
51+
to_string(coord.z) + ")";
52+
}
53+
4854
std::ostream& operator<<(std::ostream& out, const Coordinate& coord) {
49-
out << "(" << coord.x << ", " << coord.y << ", " << coord.z << ")";
55+
out << to_string(coord);
5056
return out;
5157
}
5258

59+
Chunk::Chunk(const Coordinate& loc1, const Coordinate& loc2,
60+
const std::vector<BlockType>& block_list) {
61+
Coordinate min{std::min(loc1.x, loc2.x), std::min(loc1.y, loc2.y),
62+
std::min(loc1.z, loc2.z)};
63+
this->base_pt = min.clone();
64+
65+
Coordinate dim = loc1 - loc2;
66+
x_len = std::abs(dim.x) + 1;
67+
y_len = std::abs(dim.y) + 1;
68+
z_len = std::abs(dim.z) + 1;
69+
70+
this->raw_data = new BlockType[block_list.size()];
71+
std::copy(block_list.begin(), block_list.end(), raw_data);
72+
}
73+
74+
Chunk::~Chunk() { delete[] raw_data; }
75+
76+
BlockType Chunk::get(int x, int y, int z) {
77+
if ((x < 0 || y < 0 || z < 0) ||
78+
(x > x_len - 1 || y > y_len - 1 || z > z_len - 1)) {
79+
throw std::out_of_range("Out of bounds Chunk access at " +
80+
to_string(Coordinate(x, y, z)));
81+
}
82+
return raw_data[z + z_len * (x + y_len * y)];
83+
}
84+
85+
BlockType Chunk::get_worldspace(const Coordinate& pos) {
86+
Coordinate array_pos = pos - base_pt;
87+
if ((array_pos.x < 0 || array_pos.y < 0 || array_pos.z < 0) ||
88+
(array_pos.x > x_len - 1 || array_pos.y > y_len - 1 ||
89+
array_pos.z > z_len - 1)) {
90+
throw std::out_of_range("Out of bounds Chunk access at " +
91+
to_string(array_pos) + " (world coordinate " +
92+
to_string(pos) + " )");
93+
}
94+
return raw_data[array_pos.z + z_len * (array_pos.x + y_len * array_pos.y)];
95+
}
96+
5397
HeightMap::HeightMap(const Coordinate& loc1, const Coordinate& loc2,
5498
const std::vector<int>& heights) {
5599
base_pt = Coordinate{

test/local_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ TEST_CASE("Test Coordinate class") {
7373
Coordinate testCoord(1, 2, 3);
7474
std::stringstream ss;
7575
ss << testCoord;
76-
CHECK_EQ(ss.str(), "(1, 2, 3)");
76+
CHECK_EQ(ss.str(), "(1,2,3)");
7777
}
7878
}
7979

test/minecraft_tests.cpp

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -119,32 +119,52 @@ TEST_CASE("Test the main mcpp class") {
119119
mc.setBlock(heightTestLoc, Blocks::AIR);
120120
}
121121

122-
// Used for cuboid functions
123-
Coordinate testLoc2(96, 96, 96);
124-
125-
SUBCASE("setBlocks") { mc.setBlocks(test_loc, testLoc2, Blocks::STONE); }
126-
127-
SUBCASE("getBlocks") {
128-
mc.setBlocks(test_loc, testLoc2, Blocks::DIRT);
129-
130-
auto expected = std::vector<std::vector<std::vector<BlockType>>>(
131-
5, std::vector<std::vector<BlockType>>(
132-
5, std::vector<BlockType>(5, Blocks::DIRT)));
133-
134-
std::vector returnVector = mc.getBlocks(test_loc, testLoc2);
135-
CHECK_EQ(returnVector, expected);
122+
SUBCASE("setBlocks") {
123+
Coordinate loc1{100, 100, 100};
124+
Coordinate loc2{110, 110, 110};
125+
mc.setBlocks(loc1, loc2, Blocks::STONE);
136126
}
127+
}
137128

138-
SUBCASE("getBlocks with mod") {
139-
mc.setBlocks(test_loc, testLoc2, Blocks::GRANITE);
140-
141-
auto expected = std::vector<std::vector<std::vector<BlockType>>>(
142-
5, std::vector<std::vector<BlockType>>(
143-
5, std::vector<BlockType>(5, Blocks::GRANITE)));
129+
TEST_CASE("getBlocks and Chunk operations") {
130+
// Setup
131+
Coordinate test_loc(100, 100, 100);
132+
Coordinate loc1{100, 100, 100};
133+
Coordinate loc2{110, 110, 110};
134+
135+
// Reset blocks that existed before
136+
mc.setBlocks(loc1, loc2, Blocks::AIR);
137+
mc.setBlocks(loc1, loc2, Blocks::BRICKS);
138+
mc.setBlock(loc1, Blocks::GOLD_BLOCK);
139+
mc.setBlock(loc2, Blocks::DIAMOND_BLOCK);
140+
mc.setBlock(loc1 + Coordinate{1, 2, 3}, Blocks::IRON_BLOCK);
141+
Chunk res = mc.getBlocks(loc1, loc2);
142+
143+
SUBCASE("Block accessing returns correct block using get()") {
144+
CHECK_EQ(res.get(0, 0, 0), Blocks::GOLD_BLOCK);
145+
CHECK_EQ(res.get(1, 1, 1), Blocks::BRICKS);
146+
CHECK_EQ(res.get(1, 2, 3), Blocks::IRON_BLOCK);
147+
CHECK_EQ(res.get(10, 10, 10), Blocks::DIAMOND_BLOCK);
148+
}
144149

145-
std::vector returnVector = mc.getBlocks(test_loc, testLoc2);
150+
SUBCASE("Block accessing returns correct block using get_worldspace()") {
151+
CHECK_EQ(res.get_worldspace(loc1), Blocks::GOLD_BLOCK);
152+
CHECK_EQ(res.get_worldspace(loc1 + Coordinate{1, 1, 1}),
153+
Blocks::BRICKS);
154+
CHECK_EQ(res.get_worldspace(loc1 + Coordinate{1, 2, 3}),
155+
Blocks::IRON_BLOCK);
156+
CHECK_EQ(res.get_worldspace(loc2), Blocks::DIAMOND_BLOCK);
157+
}
146158

147-
CHECK_EQ(returnVector, expected);
159+
SUBCASE("Access out of bounds correctly throws") {
160+
CHECK_THROWS(res.get(11, 0, 0));
161+
CHECK_THROWS(res.get(0, 11, 0));
162+
CHECK_THROWS(res.get(0, 0, 11));
163+
CHECK_THROWS(res.get(-1, 0, 0));
164+
CHECK_THROWS(res.get(0, -1, 0));
165+
CHECK_THROWS(res.get(0, 0, -1));
166+
CHECK_THROWS(res.get_worldspace(loc1 + Coordinate{-1, -1, -1}));
167+
CHECK_THROWS(res.get_worldspace(loc1 + Coordinate{11, 11, 11}));
148168
}
149169

150170
mc.setBlock(test_loc, BlockType(0));

0 commit comments

Comments
 (0)