The Battle for Wesnoth  1.19.0-dev
manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by Chris Beck <render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
17 
19 #include <cassert>
20 #include <stdexcept>
21 #include <string>
22 #include <vector>
23 
24 #include "log.hpp"
25 
26 static lg::log_domain log_plugins("plugins");
27 #define DBG_PLG LOG_STREAM(debug, log_plugins)
28 #define LOG_PLG LOG_STREAM(info, log_plugins)
29 #define WRN_PLG LOG_STREAM(warn, log_plugins)
30 #define ERR_PLG LOG_STREAM(err, log_plugins)
31 
32 struct plugin
33 {
34  std::string name;
35  std::string source;
36  bool is_file;
37  std::unique_ptr<application_lua_kernel::thread> thread;
38  std::vector<plugins_manager::event> queue;
39 };
40 
41 static plugins_manager * singleton = nullptr;
42 
44  : plugins_()
45  , playing_()
46  , kernel_(kernel)
47 {
48  assert(!singleton);
49  singleton = this;
50 
51  kernel_->load_core();
52  add_plugin("Null Plugin", "return function() end");
53  start_plugin(0);
54 }
55 
57 
59 {
60  return singleton;
61 }
62 
64 {
65  return kernel_.get();
66 }
67 
68 std::size_t plugins_manager::size() {
69  return plugins_.size();
70 }
71 
73  if (idx < plugins_.size()) {
74  if (!plugins_[idx].thread) {
75  return plugin_manager_status::type::not_created;
76  } else {
77  return plugins_[idx].thread->is_running() ? plugin_manager_status::type::running : plugin_manager_status::type::stopped;
78  }
79  }
80  throw std::runtime_error("index out of bounds");
81 }
82 
83 std::string plugins_manager::get_detailed_status(std::size_t idx) {
84  if (idx < plugins_.size()) {
85  if (!plugins_[idx].thread) {
86  return "not loaded";
87  } else {
88  return plugins_[idx].thread->status();
89  }
90  }
91  throw std::runtime_error("index out of bounds");
92 }
93 
94 std::string plugins_manager::get_name(std::size_t idx) {
95  if (idx < plugins_.size()) {
96  return plugins_[idx].name;
97  }
98  throw std::runtime_error("index out of bounds");
99 }
100 
101 void plugins_manager::start_plugin(std::size_t idx)
102 {
103  DBG_PLG << "start_plugin[" << idx <<"]";
104  if (idx < plugins_.size()) {
105  if (!plugins_[idx].thread) {
106  DBG_PLG << "creating thread[" << idx << "]";
107  plugins_[idx].thread.reset(plugins_[idx].is_file ?
108  kernel_->load_script_from_file(plugins_[idx].source) : kernel_->load_script_from_string(plugins_[idx].source));
109  DBG_PLG << "finished [" << idx << "], status = '" << plugins_[idx].thread->status() << "'";
110  } else {
111  DBG_PLG << "thread already exists, skipping";
112  }
113  return ;
114  }
115  throw std::runtime_error("index out of bounds");
116 }
117 
118 std::size_t plugins_manager::add_plugin(const std::string & name, const std::string & prog)
119 {
120  std::size_t idx = plugins_.size();
121  plugins_.emplace_back();
122 
123  plugin & p = plugins_[idx];
124  p.name = name;
125  p.source = prog;
126  p.is_file = false;
127 
128  return idx;
129 }
130 
131 std::size_t plugins_manager::load_plugin(const std::string & name, const std::string & filename)
132 {
133  std::size_t idx = plugins_.size();
134  plugins_.emplace_back();
135 
136  plugin & p = plugins_[idx];
137  p.name = name;
138  p.source = filename;
139  p.is_file = true;
140 
141  return idx;
142 }
143 
144 void plugins_manager::notify_event(const std::string & name, const config & data)
145 {
146  event evt;
147  evt.name = name;
148  evt.data = data;
149 
150  for (std::size_t idx = 0; idx < size(); ++idx)
151  {
152  if (plugins_[idx].thread && plugins_[idx].thread->is_running()) {
153  plugins_[idx].queue.push_back(evt);
154  }
155  }
156 }
157 
159 {
160  if (playing_) {
161  *playing_ = false; //this is to ensure "reentrancy" -- any previous calls to this function that never returned
162  //and looped back into the plugins system, should be halted and their later requests discarded
163  //this is to ensure the semantics that if a plugins context is left, then any pending requests
164  //are discarded to prevent them from being executed at an improper time
165  }
166  playing_ = std::make_shared<bool> (true);
167  std::shared_ptr<bool> local = playing_; //make a local copy of the pointer on the stack
168 
169  for (std::size_t idx = 0; idx < size(); ++idx)
170  {
171  DBG_PLG << "play_slice[" << idx << "] ...";
172  if (plugins_[idx].thread && plugins_[idx].thread->is_running()) {
173  DBG_PLG << "is running...";
174  if (!*local) { //check playing_ before each call to be sure that we should still continue
175  DBG_PLG << "aborting";
176  return;
177  }
178 
179  std::vector<event> input = plugins_[idx].queue; //empty the queue to a temporary variable
180  plugins_[idx].queue = std::vector<event>();
181 
182  //application_lua_kernel::requests_list requests =
183  std::vector<std::function<bool(void)>> requests =
184  plugins_[idx].thread->run_script(ctxt, input);
185 
186  DBG_PLG << "thread returned " << requests.size() << " requests";
187 
188  for (std::size_t j = 0; j < requests.size(); ++j) {
189  if (!*local) return; //check playing_ before each call to be sure that we should still continue
190  if (!requests[j]()) {
191  *local = false;
192  return ; //call the function but if it returns false (error) then stop
193  }
194  }
195 
196  DBG_PLG << "play_slice[" << idx << "] finished.";
197  } else if (!plugins_[idx].thread) {
198  DBG_PLG << "thread ["<< idx << "] not created";
199  } else {
200  DBG_PLG << "thread ["<< idx << "] not running";
201  }
202  }
203  *local = false;
204 }
205 
207 {
208 
209  for (std::size_t i = 0; i < size(); ++i) {
210  if (plugin_manager_status::type::running == get_status(i)) {
211  return true;
212  }
213  }
214  return false;
215 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
std::size_t size()
Definition: manager.cpp:68
void notify_event(const std::string &name, const config &data)
Definition: manager.cpp:144
std::unique_ptr< application_lua_kernel > kernel_
Definition: manager.hpp:67
plugin_manager_status::type get_status(std::size_t idx)
Definition: manager.cpp:72
std::string get_name(std::size_t idx)
Definition: manager.cpp:94
static plugins_manager * get()
Definition: manager.cpp:58
void start_plugin(std::size_t idx)
Definition: manager.cpp:101
bool any_running()
Definition: manager.cpp:206
std::size_t load_plugin(const std::string &name, const std::string &filename)
Definition: manager.cpp:131
std::string get_detailed_status(std::size_t idx)
Definition: manager.cpp:83
std::shared_ptr< bool > playing_
Definition: manager.hpp:66
void play_slice(const plugins_context &)
Definition: manager.cpp:158
lua_kernel_base * get_kernel_base()
Definition: manager.cpp:63
std::vector< plugin > plugins_
Definition: manager.hpp:65
plugins_manager(application_lua_kernel *)
Definition: manager.cpp:43
std::size_t add_plugin(const std::string &name, const std::string &prog)
Definition: manager.cpp:118
std::size_t i
Definition: function.cpp:968
Standard logging facilities (interface).
std::string_view data
Definition: picture.cpp:194
#define DBG_PLG
Definition: manager.cpp:27
static plugins_manager * singleton
Definition: manager.cpp:41
static lg::log_domain log_plugins("plugins")
std::string name
Definition: manager.cpp:34
std::vector< plugins_manager::event > queue
Definition: manager.cpp:38
std::string source
Definition: manager.cpp:35
bool is_file
Definition: manager.cpp:36
std::unique_ptr< application_lua_kernel::thread > thread
Definition: manager.cpp:37
mock_party p