Skip to content

Commit f99729d

Browse files
committed
Block placement constraints implemented. Closed #137.
1 parent 85a1b4e commit f99729d

File tree

9 files changed

+193
-3
lines changed

9 files changed

+193
-3
lines changed

docs/lua-api-block.md

+15
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,21 @@ name = "Cobblestone"
208208

209209
This label is the name that will appear everywhere in the game.
210210

211+
### `placement_constraints`
212+
213+
Constraints for the placement of the block.
214+
215+
Example:
216+
```lua
217+
placement_constraints = {
218+
-- ensure that the block below is farmland
219+
[{0, 0, -1}] = {
220+
block = "default:farmland",
221+
is_whitelist = true
222+
}
223+
}
224+
```
225+
211226
### `states`
212227

213228
Optional table containing block state definitions.

mods/default/blocks.lua

+15
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ mod:block {
364364
is_opaque = false,
365365
groups = {
366366
om_material_dirt = 1,
367+
om_farmland = 1
367368
},
368369

369370
states = {
@@ -395,6 +396,13 @@ mod:block {
395396
inventory_image = "nether_wart.png",
396397
hardness = 0,
397398

399+
placement_constraints = {
400+
[{0, 0, -1}] = {
401+
block = "default:soul_sand",
402+
is_whitelist = true
403+
}
404+
},
405+
398406
bounding_box = {0, 0, 0, 1, 1, 4.0 / 16.0},
399407
draw_offset = {0, 0, -1.0 / 16.0},
400408

@@ -436,6 +444,13 @@ mod:block {
436444
inventory_image = "seeds_wheat.png",
437445
hardness = 0,
438446

447+
placement_constraints = {
448+
[{0, 0, -1}] = {
449+
block = "default:farmland",
450+
is_whitelist = true
451+
}
452+
},
453+
439454
bounding_box = {0, 0, 0, 1, 1, 1.0 / 16.0},
440455
draw_offset = {0, 0, -1.0 / 16.0},
441456

source/client/hud/BlockCursor.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ void BlockCursor::onEvent(const SDL_Event &event, const Hotbar &hotbar) {
107107
const BlockState &newBlockState = newBlock.getState(0); // FIXME: Get state from item stack
108108
gk::FloatBox boundingBox = newBlockState.boundingBox() + gk::Vector3f(x - m_player.x(), y - m_player.y(), z - m_player.z());
109109
gk::FloatBox playerBoundingBox = m_player.hitbox();
110-
if (!boundingBox.intersects(playerBoundingBox)) {
110+
if (!boundingBox.intersects(playerBoundingBox) && newBlock.placementConstraints().check(m_world, {x, y, z})) {
111111
u32 block = hotbar.currentItem().id();
112112
if (newBlock.isRotatable()) {
113113
u16 data = m_player.getOppositeDirection() & 0x3;

source/common/world/Block.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,15 @@ const TilesDef &Block::tiles(u16 stateID) const {
4949

5050
void Block::serialize(sf::Packet &packet) const {
5151
packet << u32(m_id) << m_stringID << m_canUpdate << m_canBeActivated
52-
<< m_isRotatable << m_groups << m_states << m_param << m_customParamBits;
52+
<< m_isRotatable << m_groups << m_states << m_param << m_customParamBits
53+
<< m_placementConstraints;
5354
}
5455

5556
void Block::deserialize(sf::Packet &packet) {
5657
u32 id;
5758
packet >> id >> m_stringID >> m_canUpdate >> m_canBeActivated
58-
>> m_isRotatable >> m_groups >> m_states >> m_param >> m_customParamBits;
59+
>> m_isRotatable >> m_groups >> m_states >> m_param >> m_customParamBits
60+
>> m_placementConstraints;
5961
m_id = id;
6062

6163
for (auto &it : m_states) {

source/common/world/Block.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <gk/graphics/Color.hpp>
4040

4141
#include "BlockParam.hpp"
42+
#include "BlockPlacementConstraints.hpp"
4243
#include "BlockState.hpp"
4344
#include "ItemStack.hpp"
4445
#include "TilesDef.hpp"
@@ -91,6 +92,9 @@ class Block : public gk::ISerializable {
9192
u8 customParamBits() const { return m_customParamBits; }
9293
void setCustomParamBits(u8 customParamBits) { m_customParamBits = customParamBits; }
9394

95+
BlockPlacementConstraints &placementConstraints() { return m_placementConstraints; }
96+
const BlockPlacementConstraints &placementConstraints() const { return m_placementConstraints; }
97+
9498
static void initUsertype(sol::state &lua);
9599

96100
protected:
@@ -112,6 +116,8 @@ class Block : public gk::ISerializable {
112116
BlockParam m_param{*this};
113117

114118
u8 m_customParamBits = 0;
119+
120+
BlockPlacementConstraints m_placementConstraints;
115121
};
116122

117123
#endif // BLOCK_HPP_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* =====================================================================================
3+
*
4+
* OpenMiner
5+
*
6+
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <[email protected]>
7+
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
8+
*
9+
* This file is part of OpenMiner.
10+
*
11+
* OpenMiner is free software; you can redistribute it and/or
12+
* modify it under the terms of the GNU Lesser General Public
13+
* License as published by the Free Software Foundation; either
14+
* version 2.1 of the License, or (at your option) any later version.
15+
*
16+
* OpenMiner is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19+
* Lesser General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Lesser General Public License
22+
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
23+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24+
*
25+
* =====================================================================================
26+
*/
27+
#include "BlockPlacementConstraints.hpp"
28+
#include "Registry.hpp"
29+
#include "World.hpp"
30+
31+
bool BlockPlacementConstraints::check(const World &world, const gk::Vector3i &pos) const {
32+
bool isValid = true;
33+
for (auto &it : m_constraints) {
34+
const Block &block = Registry::getInstance().getBlockFromStringID(it.blockID);
35+
u16 blockIDToCheck = world.getBlock(
36+
pos.x + it.blockOffset.x,
37+
pos.y + it.blockOffset.y,
38+
pos.z + it.blockOffset.z);
39+
const Block &blockToCheck = Registry::getInstance().getBlock(blockIDToCheck);
40+
bool isSameBlock = (block.id() == blockToCheck.id());
41+
if ((it.isWhitelist && !isSameBlock) || (!it.isWhitelist && isSameBlock))
42+
isValid = false;
43+
}
44+
45+
return isValid;
46+
}
47+
48+
void BlockPlacementConstraints::serialize(sf::Packet &packet) const {
49+
packet << m_constraints;
50+
}
51+
52+
void BlockPlacementConstraints::deserialize(sf::Packet &packet) {
53+
packet >> m_constraints;
54+
}
55+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* =====================================================================================
3+
*
4+
* OpenMiner
5+
*
6+
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <[email protected]>
7+
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
8+
*
9+
* This file is part of OpenMiner.
10+
*
11+
* OpenMiner is free software; you can redistribute it and/or
12+
* modify it under the terms of the GNU Lesser General Public
13+
* License as published by the Free Software Foundation; either
14+
* version 2.1 of the License, or (at your option) any later version.
15+
*
16+
* OpenMiner is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19+
* Lesser General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Lesser General Public License
22+
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
23+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24+
*
25+
* =====================================================================================
26+
*/
27+
#ifndef BLOCKPLACEMENTCONSTRAINTS_HPP_
28+
#define BLOCKPLACEMENTCONSTRAINTS_HPP_
29+
30+
#include <vector>
31+
32+
#include <gk/core/ISerializable.hpp>
33+
#include <gk/core/Vector3.hpp>
34+
35+
#include "NetworkUtils.hpp"
36+
37+
struct BlockPlacementConstraint : public gk::ISerializable {
38+
gk::Vector3i blockOffset{0, 0, 0};
39+
std::string blockID;
40+
bool isWhitelist = true;
41+
42+
void serialize(sf::Packet &packet) const override { packet << blockOffset << blockID << isWhitelist; }
43+
void deserialize(sf::Packet &packet) override { packet >> blockOffset >> blockID >> isWhitelist; }
44+
};
45+
46+
class World;
47+
48+
class BlockPlacementConstraints : public gk::ISerializable {
49+
public:
50+
bool check(const World &world, const gk::Vector3i &pos) const;
51+
52+
void addConstraint(const BlockPlacementConstraint &constraint) { m_constraints.emplace_back(constraint); }
53+
54+
void serialize(sf::Packet &packet) const override;
55+
void deserialize(sf::Packet &packet) override;
56+
57+
private:
58+
std::vector<BlockPlacementConstraint> m_constraints;
59+
};
60+
61+
#endif // BLOCKPLACEMENTCONSTRAINTS_HPP_

source/server/lua/loader/LuaBlockLoader.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
*/
2727
#include <gk/core/Debug.hpp>
2828

29+
#include "BlockPlacementConstraints.hpp"
2930
#include "LuaBlockLoader.hpp"
3031
#include "LuaMod.hpp"
3132
#include "Registry.hpp"
@@ -77,6 +78,7 @@ void LuaBlockLoader::loadBlockState(BlockState &state, const sol::table &table,
7778
loadBoundingBox(state, table);
7879
loadItemDrop(state, table);
7980
loadColorMultiplier(state, table);
81+
loadPlacementConstraints(block, table);
8082

8183
loadStates(block, state, table);
8284
}
@@ -213,6 +215,39 @@ inline void LuaBlockLoader::loadStates(ServerBlock &block, BlockState &state, co
213215
}
214216
}
215217

218+
inline void LuaBlockLoader::loadPlacementConstraints(ServerBlock &block, const sol::table &table) const {
219+
sol::object constraintsObject = table["placement_constraints"];
220+
if (constraintsObject.valid()) {
221+
if (constraintsObject.get_type() == sol::type::table) {
222+
sol::table constraintsTable = constraintsObject.as<sol::table>();
223+
for (auto &constraintsObject : constraintsTable) {
224+
BlockPlacementConstraint constraint;
225+
226+
sol::optional<sol::table> blockOffset = constraintsObject.first.as<sol::table>();
227+
if (blockOffset != sol::nullopt) {
228+
constraint.blockOffset.x = blockOffset.value().get<u32>(1);
229+
constraint.blockOffset.y = blockOffset.value().get<u32>(2);
230+
constraint.blockOffset.z = blockOffset.value().get<u32>(3);
231+
}
232+
else
233+
gkError() << "For block" << block.stringID() << ": 'placement_constraints' offset is wrong";
234+
235+
sol::optional<sol::table> constraintTable = constraintsObject.second.as<sol::table>();
236+
if (constraintTable != sol::nullopt) {
237+
constraint.blockID = constraintTable.value()["block"].get<std::string>();
238+
constraint.isWhitelist = constraintTable.value()["is_whitelist"].get<bool>();
239+
}
240+
else
241+
gkError() << "For block" << block.stringID() << ": 'placement_constraints' table is wrong";
242+
243+
block.placementConstraints().addConstraint(constraint);
244+
}
245+
}
246+
else
247+
gkError() << "For block" << block.stringID() << ": 'placement_constraints' must be a table";
248+
}
249+
}
250+
216251
inline void LuaBlockLoader::loadGroups(ServerBlock &block, const sol::table &table, Item *item) const {
217252
sol::object groupsObject = table["groups"];
218253
if (groupsObject.valid()) {

source/server/lua/loader/LuaBlockLoader.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class LuaBlockLoader {
4848
void loadItemDrop(BlockState &state, const sol::table &table) const;
4949
void loadColorMultiplier(BlockState &state, const sol::table &table) const;
5050
void loadStates(ServerBlock &block, BlockState &state, const sol::table &table) const;
51+
void loadPlacementConstraints(ServerBlock &block, const sol::table &table) const;
5152
void loadGroups(ServerBlock &block, const sol::table &table, Item *item = nullptr) const;
5253
void loadParams(ServerBlock &block) const;
5354

0 commit comments

Comments
 (0)