Skip to content

Commit

Permalink
Added automatic block placement face detection (#104)
Browse files Browse the repository at this point in the history
* Added automatic block placement face detection

* Update InventoryTasks.hpp

Fix typo

* Fix const declaration + better doc

* Fix vector reserve, naming conflict, potential segfaults, missing offsets and code consistency 

This should fix all issues in my PR

* Fixed sussy maths

* Fixed sussy math #2

* Small comments refinements
  • Loading branch information
GaspardCulis authored Jun 16, 2023
1 parent f69b539 commit cf3937b
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 8 deletions.
4 changes: 2 additions & 2 deletions botcraft/include/botcraft/AI/Tasks/InventoryTasks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ namespace Botcraft
/// @param client The client performing the action
/// @param item_name The item to place
/// @param pos The location where we want to place the block
/// @param face The face on which the block is placed
/// @param face The face on which the block is placed. If not specified the optimal placing face will be automatically detected using the position of the block relative to the player eyes
/// @param wait_confirmation If true, waits for the server to send the new block in pos
/// @param allow_midair_placing If false, task will fail if the neighbour block matching face is air
/// @return Success if placement attempt was made (and confirmed by the server if wait_confirmation is true), Failure otherwise
Status PlaceBlock(BehaviourClient& client, const std::string& item_name, const Position& pos, const PlayerDiggingFace face = PlayerDiggingFace::Up, const bool wait_confirmation = false, const bool allow_midair_placing = false);
Status PlaceBlock(BehaviourClient& client, const std::string& item_name, const Position& pos, std::optional<PlayerDiggingFace> face = std::nullopt, const bool wait_confirmation = false, const bool allow_midair_placing = false);

/// @brief Same thing as PlaceBlock, but reads its parameters from the blackboard
/// @param client The client performing the action
Expand Down
55 changes: 49 additions & 6 deletions botcraft/src/AI/Tasks/InventoryTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ namespace Botcraft
return SetItemInHand(client, item_name, hand);
}

Status PlaceBlock(BehaviourClient& client, const std::string& item_name, const Position& pos, const PlayerDiggingFace face, const bool wait_confirmation, const bool allow_midair_placing)
Status PlaceBlock(BehaviourClient& client, const std::string& item_name, const Position& pos, std::optional<PlayerDiggingFace> face, const bool wait_confirmation, const bool allow_midair_placing)
{
std::shared_ptr<World> world = client.GetWorld();
std::shared_ptr<InventoryManager> inventory_manager = client.GetInventoryManager();
Expand Down Expand Up @@ -325,8 +325,51 @@ namespace Botcraft
Position(1, 0, 0), Position(-1, 0, 0)
});

// Check if block is air
bool midair_placing = true;
// If no face specified
if (!face.has_value())
{
if (allow_midair_placing) // Then we don't care and default to Up
{
face = PlayerDiggingFace::Up;
}
else
{
std::vector<PlayerDiggingFace> face_candidates; // Faces next to a block
face_candidates.reserve(6);
for (int face_idx = 0; face_idx < 6; face_idx++)
{
std::lock_guard<std::mutex> world_guard(world->GetMutex());
const Block* neighbour_block = world->GetBlock(pos + neighbour_offsets[face_idx]);
if (neighbour_block && !neighbour_block->GetBlockstate()->IsAir())
{
face_candidates.push_back(static_cast<PlayerDiggingFace>(face_idx));
}
}
if (face_candidates.size() == 0)
{
LOG_WARNING("Can't place a block in midair at " << pos);
return Status::Failure;
}
Vector3<double> player_orientation;
{
std::lock_guard<std::mutex> lock(local_player->GetMutex());
player_orientation = local_player->GetFrontVector();
}
std::sort( // Find the face face closest to player looking direction
face_candidates.begin(), face_candidates.end(), [&](PlayerDiggingFace a, PlayerDiggingFace b) -> bool
{
Vector3<double> a_offset = neighbour_offsets[static_cast<int>(a)];
Vector3<double> b_offset = neighbour_offsets[static_cast<int>(b)];
return player_orientation.dot(a_offset) > player_orientation.dot(b_offset);
// a > b because a negative dot product means the vectors are in opposite directions IE the player is looking at the face.
// But because we place the block in the inner faces we negate the result.
}
);
face = face_candidates.front(); // This does not guarantees that the choosed PlayerDiggingFace is facing the player IE player_orientation.dot(face) can be less or equal than 0.
}
} else
// Check if block is air
{
std::lock_guard<std::mutex> world_guard(world->GetMutex());

Expand All @@ -337,7 +380,7 @@ namespace Botcraft
return Status::Failure;
}

const Block* neighbour_block = world->GetBlock(pos + neighbour_offsets[static_cast<int>(face)]);
const Block* neighbour_block = world->GetBlock(pos + neighbour_offsets[static_cast<int>(face.value())]);
midair_placing = !neighbour_block || neighbour_block->GetBlockstate()->IsAir();

if (!allow_midair_placing && midair_placing)
Expand Down Expand Up @@ -381,12 +424,12 @@ namespace Botcraft
}

// If cheating is not allowed, adjust the placing position to the block containing the face we're placing against
const Position placing_pos = (allow_midair_placing && midair_placing) ? pos : (pos + neighbour_offsets[static_cast<int>(face)]);
const Position placing_pos = (allow_midair_placing && midair_placing) ? pos : (pos + neighbour_offsets[static_cast<int>(face.value())]);

std::shared_ptr<ServerboundUseItemOnPacket> place_block_msg = std::make_shared<ServerboundUseItemOnPacket>();
place_block_msg->SetLocation(placing_pos.ToNetworkPosition());
place_block_msg->SetDirection(static_cast<int>(face));
switch (face)
place_block_msg->SetDirection(static_cast<int>(face.value()));
switch (face.value())
{
case PlayerDiggingFace::Down:
place_block_msg->SetCursorPositionX(0.5f);
Expand Down

0 comments on commit cf3937b

Please # to comment.