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