Botcraft 1.21.4
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:
40 TemplatedBehaviourClient(const bool use_renderer_) :
41 BehaviourClient(use_renderer_)
42 {
43 swap_tree = false;
44 }
45
47 {
48 // Make sure should_be_closed is set to false
49 // otherwise the behaviour_thread will not exit
50 // properly
51 {
52 std::lock_guard<std::mutex> behaviour_guard(behaviour_mutex);
53 should_be_closed = true;
54 }
55
56 behaviour_cond_var.notify_all();
57 if (behaviour_thread.joinable())
58 {
59 behaviour_thread.join();
60 }
61 }
62
63 /// @brief Save the given tree to replace the current one as soon as possible.
64 /// @param tree_ The new tree
65 /// @param blackboard_ Initial values to put into the blackboard when swapping tree
66 void SetBehaviourTree(const std::shared_ptr<BehaviourTree<TDerived> >& tree_, const std::map<std::string, std::any>& blackboard_ = {})
67 {
68 std::lock_guard<std::mutex> behaviour_guard(behaviour_mutex);
69 swap_tree = true;
70 new_tree = tree_;
71 new_blackboard = blackboard_;
72 }
73
74 /// @brief Can be called to pause the execution of the internal
75 /// tree function. Call it in long function so the behaviour
76 /// can be interrupted.
77 virtual void Yield() override
78 {
79 std::unique_lock<std::mutex> lock(behaviour_mutex);
80 behaviour_cond_var.notify_all();
81 behaviour_cond_var.wait(lock);
83 {
84 throw Interrupted();
85 }
86 else if (swap_tree)
87 {
88 throw SwapTree();
89 }
90 }
91
92 /// @brief Start the behaviour thread loop.
94 {
95 tree_loop_ready = false;
97
98 // Wait for the thread to start and ready to run
99 while (!tree_loop_ready)
100 {
101 Utilities::SleepFor(std::chrono::milliseconds(10));
102 }
103 }
104
105 /// @brief Blocking call, will return only when the client is
106 /// disconnected from the server.
108 {
109 if (!behaviour_thread.joinable())
110 {
112 }
113
114 // Main behaviour loop
115 while (!should_be_closed)
116 {
117 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
118 std::chrono::steady_clock::time_point end = start + std::chrono::milliseconds(10);
119
121
123 }
124 }
125
126 /// @brief Perform one step of the behaviour tree.
127 /// Don't forget to call StartBehaviour before.
129 {
131 {
132 return;
133 }
134
135 std::unique_lock<std::mutex> lock(behaviour_mutex);
136 // Resume tree ticking
137 behaviour_cond_var.notify_all();
138 // Wait for the next call to Yield()
139 behaviour_cond_var.wait(lock);
140 }
141
142 /// @brief Set a tree to execute the given action once and block until done.
143 /// This will change the current tree. BehaviourStep should **NOT** be called
144 /// by another thread simultaneously. It means you should **NOT** call
145 /// RunBehaviourUntilClosed when using this sync version.
146 /// @param timeout_ms Max running time of the function in ms, ignored if 0
147 /// @param ...args Parameters passed to create tree leaf
148 template<typename... Args>
149 void SyncAction(const int timeout_ms, Args&&... args)
150 {
151 // Make sure the behaviour thread is running
152 if (!behaviour_thread.joinable())
153 {
155 }
156
157 // Set the tree
159 .sequence()
160 .succeeder().leaf(std::forward<Args>(args)...)
161 .leaf([](TDerived& c) { c.SetBehaviourTree(nullptr); return Status::Success; })
162 .end());
163
164 // Perform one step to get out of the Yield lock and swap tree
166
167 // Wait for the tree to be set as active one
169 {
170 return tree != nullptr;
171 }, 500))
172 {
173 LOG_WARNING("Timeout waiting for tree swapping in SyncAction");
174 return;
175 }
176
177 // Run until tree is ticked once and reset to nullptr
178 const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
179 while (tree != nullptr && !should_be_closed)
180 {
181 std::chrono::steady_clock::time_point iter_start = std::chrono::steady_clock::now();
182 std::chrono::steady_clock::time_point iter_end = iter_start + std::chrono::milliseconds(10);
183
184 // Timeout, reset tree and get out
185 if (timeout_ms > 0 && std::chrono::duration_cast<std::chrono::milliseconds>(iter_start - start).count() > timeout_ms)
186 {
187 LOG_WARNING("Timeout when doing SyncAction");
188 SetBehaviourTree(nullptr);
191 {
192 return tree == nullptr;
193 }, 500);
194 break;
195 }
196
198
199 Utilities::SleepUntil(iter_end);
200 }
201 }
202
203
204 /// @brief Set a tree to execute the given action once and block until done.
205 /// This will change the current tree. BehaviourStep should **NOT** be called
206 /// by another thread simultaneously. It means you should **NOT** call
207 /// RunBehaviourUntilClosed when using this sync version. This version has no
208 /// timeout and will run until the tree ends.
209 /// @param ...args Parameters passed to create tree leaf
210 template<typename... Args>
211 void SyncAction(Args&&... args)
212 {
213 return SyncAction(0, std::forward<Args>(args)...);
214 }
215
216 void OnTreeChanged(const BaseNode* root)
217 {
218 const std::string& tree_name = root != nullptr ? root->GetName() : "nullptr";
219 LOG_INFO("Behaviour tree changed" << (tree_name.empty() ? " (anonymous tree)" : (" to " + tree_name)));
220#if USE_GUI
221 if (rendering_manager != nullptr)
222 {
223 rendering_manager->SetCurrentBehaviourTree(root);
224 }
225#endif
226 }
227
228#if USE_GUI
230 {
231 if (rendering_manager != nullptr)
232 {
233 rendering_manager->ResetBehaviourState();
234 }
235 }
236
238 {
239 if (rendering_manager != nullptr)
240 {
241 rendering_manager->BehaviourStartTick();
242 while (rendering_manager->IsBehaviourGUIPaused())
243 {
244 Yield();
245 }
246 }
247 }
248
249 void OnNodeEndTick(const Status s)
250 {
251 if (rendering_manager != nullptr)
252 {
253 rendering_manager->BehaviourEndTick(s == Status::Success);
254 }
255 }
256
257 void OnNodeTickChild(const size_t i)
258 {
259 if (rendering_manager != nullptr)
260 {
261 rendering_manager->BehaviourTickChild(i);
262 }
263 }
264#endif
265
266 private:
267 void TreeLoop()
268 {
269 Logger::GetInstance().RegisterThread("Behaviour - " + GetNetworkManager()->GetMyName());
270 tree_loop_ready = true;
271 while (true)
272 {
273 try
274 {
275 if (tree)
276 {
277#if USE_GUI
279#endif
280 tree->Tick(static_cast<TDerived&>(*this));
281 }
282 Yield();
283 }
284 // We need to update the tree with the new one
285 catch (const SwapTree&)
286 {
287 tree = new_tree;
288 new_tree = nullptr;
289 swap_tree = false;
290 OnTreeChanged(tree.get());
292 continue;
293 }
294 // We need to stop the behaviour thread
295 catch (const Interrupted&)
296 {
297 return;
298 }
299 catch (const std::exception& e)
300 {
301 LOG_ERROR("Exception caught during tree ticking:\n" << e.what() << "\nStopping behaviour.");
302 return;
303 }
304 catch (...)
305 {
306 LOG_ERROR("Unknown exception caught during tree ticking. Stopping behaviour.");
307 return;
308 }
309 }
310 }
311
312 private:
313 std::shared_ptr<BehaviourTree<TDerived> > tree;
314 std::shared_ptr<BehaviourTree<TDerived> > new_tree;
315 std::map<std::string, std::any> new_blackboard;
317
318 std::thread behaviour_thread;
319 std::condition_variable behaviour_cond_var;
320 std::mutex behaviour_mutex;
321
322 std::atomic<bool> tree_loop_ready;
323 };
324} // 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.
TemplatedBehaviourClient(const bool use_renderer_)
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)