Botcraft 1.21.4
Loading...
Searching...
No Matches
DigTask.cpp
Go to the documentation of this file.
5
16
17
18using namespace ProtocolCraft;
19
20namespace Botcraft
21{
22 Status DigImpl(BehaviourClient& c, const Position& pos, const bool send_swing, const PlayerDiggingFace face, const bool allow_pathfinding)
23 {
24 std::shared_ptr<LocalPlayer> local_player = c.GetLocalPlayer();
25
26 std::shared_ptr<World> world = c.GetWorld();
27 const Blockstate* blockstate = world->GetBlock(pos);
28
29 // Not loaded, pathfind toward it
30 if (blockstate == nullptr)
31 {
32 if (!allow_pathfinding || GoTo(c, pos, 4) == Status::Failure)
33 {
34 return Status::Failure;
35 }
36 // Update the block after pathfinding
37 blockstate = world->GetBlock(pos);
38 }
39
40 // This should not happen as the block should be loaded if we successfully moved near it but just in case
41 if (blockstate == nullptr)
42 {
43 LOG_WARNING("Digging target block is nullptr while being close to player");
44 return Status::Failure;
45 }
46
47 // Nothing to do
48 if (blockstate->IsAir())
49 {
50 return Status::Success;
51 }
52
53 const GameType game_mode = local_player->GetGameMode();
54
55 if (blockstate->IsFluid() || // Can't dig fluids
56 game_mode == GameType::Adventure || game_mode == GameType::Spectator || // Can't break block in adventure or spectator mode
57 (!local_player->GetInstabuild() && blockstate->GetHardness() < 0.0f) // Non breakable blocks without creative instabuild flag
58 )
59 {
60 return Status::Failure;
61 }
62
63 // Check if the block is in range
64#if PROTOCOL_VERSION < 766 /* < 1.20.5 */
65 const double range = game_mode == GameType::Creative ? 5.0 : 4.5;
66#else
67 const double range = static_cast<double>(local_player->GetAttributePlayerBlockInteractionRangeValue());
68#endif
69 Vector3<double> eye_pos = local_player->GetPosition() + Vector3<double>(0.0, local_player->GetEyeHeight(), 0.0);
70 Vector3<double> closest_point_on_block = blockstate->GetClosestPoint(pos, eye_pos);
71
72 int pathfind_tolerance = 4;
73 while (pathfind_tolerance > 0 && eye_pos.SqrDist(closest_point_on_block) >= range * range)
74 {
75 if (!allow_pathfinding || GoTo(c, pos, pathfind_tolerance) == Status::Failure)
76 {
77 return Status::Failure;
78 }
79 eye_pos = local_player->GetPosition() + Vector3<double>(0.0, local_player->GetEyeHeight(), 0.0);
80 closest_point_on_block = blockstate->GetClosestPoint(pos, eye_pos);
81 pathfind_tolerance -= 1;
82 }
83
84 // We didn't manage to get close enough
85 if (eye_pos.SqrDist(closest_point_on_block) >= range * range)
86 {
87 return Status::Failure;
88 }
89
90 const Position eyes_block = Position(
91 static_cast<int>(std::floor(eye_pos.x)),
92 static_cast<int>(std::floor(eye_pos.y)),
93 static_cast<int>(std::floor(eye_pos.z))
94 );
95 const bool is_on_ground = local_player->GetOnGround();
96
97 const Blockstate* head_blockstate = world->GetBlock(eyes_block);
98 const bool is_head_in_fluid = head_blockstate != nullptr && head_blockstate->IsFluid();
99
100 ToolType current_tool_type = ToolType::None;
101 ToolMaterial current_tool_material = ToolMaterial::None;
102#if PROTOCOL_VERSION < 767 /* < 1.21 */
103 float efficiency_speed_boost = 0.0f;
104#else
105 const float efficiency_speed_boost = static_cast<float>(local_player->GetAttributePlayerMiningEfficiencyValue());
106#endif
107#if PROTOCOL_VERSION < 767 /* < 1.21 */
108 float submerged_speed_multiplier = 1.0f;
109#else
110 float submerged_speed_multiplier = is_head_in_fluid ? static_cast<float>(local_player->GetAttributePlayerSubmergedMiningSpeedValue()) : 1.0f;
111#endif
112 if (!local_player->GetInstabuild())
113 {
114 std::shared_ptr<InventoryManager> inventory_manager = c.GetInventoryManager();
115
116#if PROTOCOL_VERSION < 767 /* < 1.21 */
117 // Check if we have aqua affinity
118 if (is_head_in_fluid)
119 {
120 const Slot head_armor = inventory_manager->GetPlayerInventory()->GetSlot(Window::INVENTORY_HEAD_ARMOR);
122 {
123 submerged_speed_multiplier = 0.2f;
124 }
125 }
126#endif
127
128 // Get tool properties
129 const Slot main_hand = inventory_manager->GetHotbarSelected();
130 if (!main_hand.IsEmptySlot())
131 {
132 const Item* item;
133#if PROTOCOL_VERSION < 347 /* < 1.13 */
134 item = AssetsManager::getInstance().GetItem(std::pair<int, unsigned char>{ main_hand.GetBlockId(), static_cast<unsigned char>(main_hand.GetItemDamage()) });
135#else
136 item = AssetsManager::getInstance().GetItem(main_hand.GetItemId());
137#endif
138 if (item != nullptr)
139 {
140 current_tool_type = item->GetToolType();
141 current_tool_material = item->GetToolMaterial();
142 }
143#if PROTOCOL_VERSION < 767 /* < 1.21 */
144 const short efficiency_level = static_cast<unsigned char>(Utilities::GetEnchantmentLvl(main_hand, Enchantment::Efficiency));
145 efficiency_speed_boost = (efficiency_level > 0) * 1.0f + efficiency_level * efficiency_level;
146#endif
147 }
148 }
149
150 unsigned char haste_amplifier = 0;
151 unsigned char mining_fatigue_amplifier = 0;
152 if (!local_player->GetInstabuild())
153 {
154 for (const auto& effect : local_player->GetEffects())
155 {
156 if (effect.type == EntityEffectType::Haste && effect.end > std::chrono::steady_clock::now())
157 {
158 haste_amplifier = effect.amplifier + 1; // amplifier is 0 for "Effect I"
159 continue;
160 }
161 if (effect.type == EntityEffectType::MiningFatigue && effect.end > std::chrono::steady_clock::now())
162 {
163 mining_fatigue_amplifier = effect.amplifier + 1;
164 continue;
165 }
166 }
167 }
168
169#if PROTOCOL_VERSION < 766 /* < 1.20.5 */
170 constexpr float speed_multiplier = 1.0f;
171#else
172 const float speed_multiplier = static_cast<float>(local_player->GetAttributePlayerBlockBreakSpeedValue());
173#endif
174
175 const double ms_per_tick = c.GetPhysicsManager()->GetMsPerTick();
176 const float expected_mining_time_s =
177 local_player->GetInstabuild() ? 0.0f :
178 blockstate->GetMiningTimeSeconds(
179 current_tool_type,
180 current_tool_material,
181 efficiency_speed_boost,
182 haste_amplifier,
183 mining_fatigue_amplifier,
184 is_on_ground,
185 speed_multiplier * submerged_speed_multiplier
186 ) * static_cast<float>(ms_per_tick / 50.0);
187 if (expected_mining_time_s > 60.0f)
188 {
189 LOG_INFO("Starting an expected " << expected_mining_time_s << " seconds long mining at " << pos << ".A little help?");
190 }
191
192 // TODO check line of sight
193 // Look at block
194 // TODO look at model AABB center instead of full block center
195 local_player->LookAt(Vector3<double>(0.5, 0.5, 0.5) + pos, true);
196
197 std::shared_ptr<NetworkManager> network_manager = c.GetNetworkManager();
198 std::shared_ptr<ServerboundPlayerActionPacket> msg_digging = std::make_shared<ServerboundPlayerActionPacket>();
199 msg_digging->SetAction(static_cast<int>(PlayerDiggingStatus::StartDigging));
200 msg_digging->SetPos(pos.ToNetworkPosition());
201 msg_digging->SetDirection(static_cast<int>(face));
202#if PROTOCOL_VERSION > 758 /* > 1.18.2 */
203 msg_digging->SetSequence(world->GetNextWorldInteractionSequenceId());
204#endif
205 network_manager->Send(msg_digging);
206
207 std::shared_ptr<ServerboundSwingPacket> swing_packet;
208 std::chrono::steady_clock::time_point last_time_send_swing;
209 if (send_swing)
210 {
211 swing_packet = std::make_shared<ServerboundSwingPacket>();
212 swing_packet->SetHand(static_cast<int>(Hand::Main));
213 network_manager->Send(swing_packet);
214 last_time_send_swing = std::chrono::steady_clock::now();
215 }
216
217 auto start = std::chrono::steady_clock::now();
218 bool finished_sent = local_player->GetInstabuild(); // In creative mode we don't need to send a finish digging packet
219 while (true)
220 {
221 auto now = std::chrono::steady_clock::now();
222 long long int elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
223 if (elapsed > 50 * ms_per_tick + expected_mining_time_s * 1000.0f)
224 {
225 LOG_WARNING("Something went wrong waiting block breaking confirmation (Timeout).");
226 return Status::Failure;
227 }
228 if (elapsed >= expected_mining_time_s * 1000.0f
229 && !finished_sent)
230 {
231 std::shared_ptr<ServerboundPlayerActionPacket> msg_finish(new ServerboundPlayerActionPacket);
232 msg_finish->SetAction(static_cast<int>(PlayerDiggingStatus::FinishDigging));
233 msg_finish->SetPos(pos.ToNetworkPosition());
234 msg_finish->SetDirection(static_cast<int>(face));
235#if PROTOCOL_VERSION > 758 /* > 1.18.2 */
236 msg_finish->SetSequence(world->GetNextWorldInteractionSequenceId());
237#endif
238 network_manager->Send(msg_finish);
239
240 finished_sent = true;
241 }
242 if (send_swing && !finished_sent && std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time_send_swing).count() > 5.0 * ms_per_tick)
243 {
244 last_time_send_swing = now;
245 network_manager->Send(swing_packet);
246 }
247 const Blockstate* block = world->GetBlock(pos);
248
249 if (block == nullptr || block->IsAir())
250 {
251 return Status::Success;
252 }
253 c.Yield();
254 }
255
256 return Status::Success;
257 }
258
259 Status Dig(BehaviourClient& c, const Position& pos, const bool send_swing, const PlayerDiggingFace face, const bool allow_pathfinding)
260 {
261 constexpr std::array variable_names = {
262 "Dig.pos",
263 "Dig.send_swing",
264 "Dig.face",
265 "Dig.allow_pathfinding"
266 };
267
268 Blackboard& blackboard = c.GetBlackboard();
269
270 blackboard.Set<Position>(variable_names[0], pos);
271 blackboard.Set<bool>(variable_names[1], send_swing);
272 blackboard.Set<PlayerDiggingFace>(variable_names[2], face);
273 blackboard.Set<bool>(variable_names[3], allow_pathfinding);
274
275 return DigImpl(c, pos, send_swing, face, allow_pathfinding);
276 }
277
279 {
280 constexpr std::array variable_names = {
281 "Dig.pos",
282 "Dig.send_swing",
283 "Dig.face",
284 "Dig.allow_pathfinding"
285 };
286
287 Blackboard& blackboard = c.GetBlackboard();
288
289 // Mandatory
290 const Position& pos = blackboard.Get<Position>(variable_names[0]);
291
292 // Optional
293 const bool send_swing = blackboard.Get<bool>(variable_names[1], false);
294 const PlayerDiggingFace face = blackboard.Get<PlayerDiggingFace>(variable_names[2], PlayerDiggingFace::Up);
295 const bool allow_pathfinding = blackboard.Get<bool>(variable_names[3], true);
296
297 return DigImpl(c, pos, send_swing, face, allow_pathfinding);
298 }
299}
#define LOG_WARNING(osstream)
Definition Logger.hpp:44
#define LOG_INFO(osstream)
Definition Logger.hpp:43
const Item * GetItem(const ItemId id) const
static AssetsManager & getInstance()
A ManagersClient extended with a blackboard that can store any kind of data and a virtual Yield funct...
virtual void Yield()=0
A map wrapper to store arbitrary data.
void Set(const std::string &key, const T &value)
Set map entry at key to value.
const T & Get(const std::string &key)
Get the map value at key, casting it to T.
Vector3< double > GetClosestPoint(const Position &block_pos, const Vector3< double > &pos) const
Get the closest point on this blockstate placed at block_pos from a reference pos.
float GetHardness() const
float GetMiningTimeSeconds(const ToolType tool_type, const ToolMaterial tool_material, const float tool_efficiency_additional_speed=0.0f, const unsigned char haste=0, const unsigned char fatigue=0, const bool on_ground=true, const float speed_factor=1.0f) const
Compute the amount of time (in s) required to mine this block.
std::shared_ptr< NetworkManager > GetNetworkManager() const
ToolMaterial GetToolMaterial() const
Definition Item.cpp:36
ToolType GetToolType() const
Definition Item.cpp:31
std::shared_ptr< PhysicsManager > GetPhysicsManager() const
std::shared_ptr< LocalPlayer > GetLocalPlayer() const
std::shared_ptr< InventoryManager > GetInventoryManager() const
std::shared_ptr< World > GetWorld() const
static constexpr short INVENTORY_HEAD_ARMOR
Definition Window.hpp:21
bool IsEmptySlot() const
Definition Slot.hpp:92
short GetEnchantmentLvl(const ProtocolCraft::Slot &item, const Botcraft::Enchantment enchantment)
Status GoTo(BehaviourClient &client, const Position &goal, const int dist_tolerance=0, const int min_end_dist=0, const int min_end_dist_xz=0, const bool allow_jump=true, const bool sprint=true, const float speed_factor=1.0f)
Find a path to a block position and navigate to it.
Vector3< int > Position
Definition Vector3.hpp:282
Status DigImpl(BehaviourClient &c, const Position &pos, const bool send_swing, const PlayerDiggingFace face, const bool allow_pathfinding)
Definition DigTask.cpp:22
Status Dig(BehaviourClient &c, const Position &pos, const bool send_swing=false, const PlayerDiggingFace face=PlayerDiggingFace::Up, const bool allow_pathfinding=true)
Dig a block at a given location.
Definition DigTask.cpp:259
Status DigBlackboard(BehaviourClient &c)
Same thing as Dig, but reads its parameters from the blackboard.
Definition DigTask.cpp:278
double SqrDist(const Vector3 &v) const
Definition Vector3.hpp:192
ProtocolCraft::NetworkPosition ToNetworkPosition() const
Definition Vector3.hpp:272