Botcraft 1.21.11
Loading...
Searching...
No Matches
TemplatedBehaviourClient.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <atomic>
4
10#if USE_GUI
12#endif
13
14namespace Botcraft
15{
16 /// @brief The base class you should inherit if you need to
17 /// implement some custom Handle functions AND need to add
18 /// custom fields to your derived class. If you just need
19 /// some custom Handle, inheriting SimpleBehaviourClient is
20 /// sufficient.
21 /// @tparam TDerived Any class inheriting this class (see SimpleBehaviourClient)
22 template<typename TDerived>
24 {
25 private:
26 /// @brief Custom internal type used when the tree needs
27 /// to be changed. It does not inherit std::exception to
28 /// prevent tree components from catching it
30 {
31 };
32 /// @brief Custom internal type used when the tree needs
33 /// to be stopped. It does not inherit std::exception to
34 /// prevent tree components from catching it
36 {
37 };
38
39 public:
44
46 {
47 // Make sure should_be_closed is set to false
48 // otherwise the behaviour_thread will not exit
49 // properly
50 {
51 std::lock_guard<std::mutex> behaviour_guard(behaviour_mutex);
52 should_be_closed = true;
53 }
54
55 behaviour_cond_var.notify_all();
56 if (behaviour_thread.joinable())
57 {
58 behaviour_thread.join();
59 }
60 }
61
62 /// @brief Save the given tree to replace the current one as soon as possible.
63 /// @param tree_ The new tree
64 /// @param blackboard_ Initial values to put into the blackboard when swapping tree
65 void SetBehaviourTree(const std::shared_ptr<BehaviourTree<TDerived> >& tree_, const std::map<std::string, std::any>& blackboard_ = {})
66 {
67 std::lock_guard<std::mutex> behaviour_guard(behaviour_mutex);
68 swap_tree = true;
69 new_tree = tree_;
70 new_blackboard = blackboard_;
71 }
72
73 /// @brief Can be called to pause the execution of the internal
74 /// tree function. Call it in long function so the behaviour
75 /// can be interrupted.
76 virtual void Yield() override
77 {
78 std::unique_lock<std::mutex> lock(behaviour_mutex);
79 behaviour_cond_var.notify_all();
80 behaviour_cond_var.wait(lock);
82 {
83 throw Interrupted();
84 }
85 else if (swap_tree)
86 {
87 throw SwapTree();
88 }
89 }
90
91 /// @brief Start the behaviour thread loop.
93 {
94 tree_loop_ready = false;
96
97 // Wait for the thread to start and ready to run
98 while (!tree_loop_ready)
99 {
100 Utilities::SleepFor(std::chrono::milliseconds(10));
101 }
102 }
103
104 /// @brief Blocking call, will return only when the client is
105 /// disconnected from the server.
107 {
108 if (!behaviour_thread.joinable())
109 {
111 }
112
113 // Main behaviour loop
114 while (!should_be_closed)
115 {
116 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
117 std::chrono::steady_clock::time_point end = start + std::chrono::milliseconds(10);
118
120
122 }
123 }
124
125 /// @brief Perform one step of the behaviour tree.
126 /// Don't forget to call StartBehaviour before.
128 {
130 {
131 return;
132 }
133
134 std::unique_lock<std::mutex> lock(behaviour_mutex);
135 // Resume tree ticking
136 behaviour_cond_var.notify_all();
137 // Wait for the next call to Yield()
138 behaviour_cond_var.wait(lock);
139 }
140
141 /// @brief Set a tree to execute the given action once and block until done.
142 /// This will change the current tree. BehaviourStep should **NOT** be called
143 /// by another thread simultaneously. It means you should **NOT** call
144 /// RunBehaviourUntilClosed when using this sync version.
145 /// @param timeout_ms Max running time of the function in ms, ignored if 0
146 /// @param ...args Parameters passed to create tree leaf
147 template<typename... Args>
148 void SyncAction(const int timeout_ms, Args&&... args)
149 {
150 // Make sure the behaviour thread is running
151 if (!behaviour_thread.joinable())
152 {
154 }
155
156 // Set the tree
158 .sequence()
159 .succeeder().leaf(std::forward<Args>(args)...)
160 .leaf([](TDerived& c) { c.SetBehaviourTree(nullptr); return Status::Success; })
161 .end());
162
163 // Perform one step to get out of the Yield lock and swap tree
165
166 // Wait for the tree to be set as active one
168 {
169 return tree != nullptr;
170 }, 500))
171 {
172 LOG_WARNING("Timeout waiting for tree swapping in SyncAction");
173 return;
174 }
175
176 // Run until tree is ticked once and reset to nullptr
177 const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
178 while (tree != nullptr && !should_be_closed)
179 {
180 std::chrono::steady_clock::time_point iter_start = std::chrono::steady_clock::now();
181 std::chrono::steady_clock::time_point iter_end = iter_start + std::chrono::milliseconds(10);
182
183 // Timeout, reset tree and get out
184 if (timeout_ms > 0 && std::chrono::duration_cast<std::chrono::milliseconds>(iter_start - start).count() > timeout_ms)
185 {
186 LOG_WARNING("Timeout when doing SyncAction");
187 SetBehaviourTree(nullptr);
190 {
191 return tree == nullptr;
192 }, 500);
193 break;
194 }
195
197
198 Utilities::SleepUntil(iter_end);
199 }
200 }
201
202
203 /// @brief Set a tree to execute the given action once and block until done.
204 /// This will change the current tree. BehaviourStep should **NOT** be called
205 /// by another thread simultaneously. It means you should **NOT** call
206 /// RunBehaviourUntilClosed when using this sync version. This version has no
207 /// timeout and will run until the tree ends.
208 /// @param ...args Parameters passed to create tree leaf
209 template<typename... Args>
210 void SyncAction(Args&&... args)
211 {
212 return SyncAction(0, std::forward<Args>(args)...);
213 }
214
215 void OnTreeChanged(const BaseNode* root)
216 {
217 const std::string& tree_name = root != nullptr ? root->GetName() : "nullptr";
218 LOG_INFO("Behaviour tree changed" << (tree_name.empty() ? " (anonymous tree)" : (" to " + tree_name)));
219#if USE_GUI
220 if (rendering_manager != nullptr)
221 {
222 rendering_manager->SetCurrentBehaviourTree(root);
223 }
224#endif
225 }
226
227#if USE_GUI
229 {
230 if (rendering_manager != nullptr)
231 {
232 rendering_manager->ResetBehaviourState();
233 }
234 }
235
237 {
238 if (rendering_manager != nullptr)
239 {
240 rendering_manager->BehaviourStartTick();
241 while (rendering_manager->IsBehaviourGUIPaused())
242 {
243 Yield();
244 }
245 }
246 }
247
248 void OnNodeEndTick(const Status s)
249 {
250 if (rendering_manager != nullptr)
251 {
252 rendering_manager->BehaviourEndTick(s == Status::Success);
253 }
254 }
255
256 void OnNodeTickChild(const size_t i)
257 {
258 if (rendering_manager != nullptr)
259 {
260 rendering_manager->BehaviourTickChild(i);
261 }
262 }
263#endif
264
265 private:
266 void TreeLoop()
267 {
268 Logger::GetInstance().RegisterThread("Behaviour - " + GetNetworkManager()->GetMyName());
269 tree_loop_ready = true;
270 while (true)
271 {
272 try
273 {
274 if (tree)
275 {
276#if USE_GUI
278#endif
279 tree->Tick(static_cast<TDerived&>(*this));
280 }
281 Yield();
282 }
283 // We need to update the tree with the new one
284 catch (const SwapTree&)
285 {
286 {
287 std::lock_guard<std::mutex> behaviour_guard(behaviour_mutex);
288 tree = new_tree;
289 new_tree = nullptr;
290 swap_tree = false;
291 OnTreeChanged(tree.get());
293 }
294 continue;
295 }
296 // We need to stop the behaviour thread
297 catch (const Interrupted&)
298 {
299 return;
300 }
301 catch (const std::exception& e)
302 {
303 LOG_ERROR("Exception caught during tree ticking:\n" << e.what() << "\nStopping behaviour.");
304 return;
305 }
306 catch (...)
307 {
308 LOG_ERROR("Unknown exception caught during tree ticking. Stopping behaviour.");
309 return;
310 }
311 }
312 }
313
314 private:
315 std::shared_ptr<BehaviourTree<TDerived> > tree;
316 std::shared_ptr<BehaviourTree<TDerived> > new_tree;
317 std::map<std::string, std::any> new_blackboard;
319
320 std::thread behaviour_thread;
321 std::condition_variable behaviour_cond_var;
322 std::mutex behaviour_mutex;
323
324 std::atomic<bool> tree_loop_ready;
325 };
326} // namespace Botcraft
#define LOG_ERROR(osstream)
Definition Logger.hpp:45
#define LOG_WARNING(osstream)
Definition Logger.hpp:44
#define LOG_INFO(osstream)
Definition Logger.hpp:43
const std::string & GetName() const
Definition BaseNode.cpp:24
A ManagersClient extended with a blackboard that can store any kind of data and a virtual Yield funct...
void Reset(const std::map< std::string, std::any > &values={})
Clear all the entries in the blackboard and load new ones.
std::shared_ptr< NetworkManager > GetNetworkManager() const
std::shared_ptr< NetworkManager > network_manager
void RegisterThread(const std::string &name)
Register the current thread in the map.
Definition Logger.cpp:104
static Logger & GetInstance()
Definition Logger.cpp:36
std::shared_ptr< Renderer::RenderingManager > rendering_manager
Custom internal type used when the tree needs to be stopped.
Custom internal type used when the tree needs to be changed.
The base class you should inherit if you need to implement some custom Handle functions AND need to a...
void SyncAction(const int timeout_ms, Args &&... args)
Set a tree to execute the given action once and block until done.
void StartBehaviour()
Start the behaviour thread loop.
void RunBehaviourUntilClosed()
Blocking call, will return only when the client is disconnected from the server.
virtual void Yield() override
Can be called to pause the execution of the internal tree function.
std::map< std::string, std::any > new_blackboard
void SetBehaviourTree(const std::shared_ptr< BehaviourTree< TDerived > > &tree_, const std::map< std::string, std::any > &blackboard_={})
Save the given tree to replace the current one as soon as possible.
void BehaviourStep()
Perform one step of the behaviour tree.
std::shared_ptr< BehaviourTree< TDerived > > new_tree
std::shared_ptr< BehaviourTree< TDerived > > tree
void SyncAction(Args &&... args)
Set a tree to execute the given action once and block until done.
void SleepUntil(const std::chrono::steady_clock::time_point &end)
bool WaitForCondition(const std::function< bool()> &condition, const long long int timeout_ms=0, const long long int check_interval_ms=10)
void SleepFor(const std::chrono::duration< _Rep, _Period > &time)