Leviathan  0.8.0.0
Leviathan game engine
GameModule.cpp
Go to the documentation of this file.
1 // ------------------------------------ //
2 #include "GameModule.h"
3 
5 #include "FileSystem.h"
6 #include "GameModuleLoader.h"
9 
10 #include <boost/filesystem.hpp>
11 using namespace Leviathan;
12 // ------------------------------------ //
13 DLLEXPORT GameModule::GameModule(const std::string& filepath, const std::string& ownername,
14  GameModuleLoader* loadedthrough) :
15  EventableScriptObject(nullptr),
16  OwnerName(ownername), Loader(loadedthrough)
17 {
18  std::string file = filepath;
19 
20  if(!boost::filesystem::is_regular_file(file)) {
21 
22  // Couldn't find file //
23  throw InvalidArgument("File doesn't exist '" + filepath + "'");
24  }
25 
26  LoadedFromFile = file;
27 
28  // Load the file //
30 
31  if(!ofile) {
32 
33  throw InvalidArgument("File is invalid");
34  }
35 
36  // Process the objects //
37  if(ofile->GetTotalObjectCount() != 1) {
38 
39  throw InvalidArgument("File contains invalid number of objects, single GameModule "
40  "expected");
41  }
42 
43  // Get various data from the header //
44  ObjectFileProcessor::LoadValueFromNamedVars<std::string>(
45  ofile->GetVariables(), "Version", Name, "-1", Logger::Get(), "GameModule:");
46 
47  auto gmobject = ofile->GetObjectFromIndex(0);
48 
49  Name = gmobject->GetName();
50 
51  // Handle the single object //
52  ObjectFileList* properties = gmobject->GetListWithName("properties");
53  ObjectFileTextBlock* sources = gmobject->GetTextBlockWithName("sourcefiles");
54  ObjectFileTextBlock* exports = gmobject->GetTextBlockWithName("export");
55  ObjectFileTextBlock* imports = gmobject->GetTextBlockWithName("import");
56 
57  if(!properties || !sources) {
58 
59  throw InvalidArgument("File contains invalid GameModule, properties or "
60  "sourcefiles not found");
61  }
62 
63  // Copy data //
64  if(sources->GetLineCount() < 1) {
65 
66  throw InvalidArgument("At least one source file expected in sourcefiles (even if this "
67  "module only consists of public definitions)");
68  }
69 
70  const std::string moduleFilePath =
71  boost::filesystem::path(StringOperations::GetPath(LoadedFromFile)).generic_string();
72 
73  // Resolve all files to their actual paths //
74  for(size_t i = 0; i < sources->GetLineCount(); ++i) {
75 
76  // Skip empty lines to allow commenting out things //
77  if(sources->GetLine(i).empty())
78  continue;
79 
80  const std::string codeFile =
81  ScriptModule::ResolvePathToScriptFile(sources->GetLine(i), moduleFilePath);
82 
83  if(codeFile.empty()) {
84  throw InvalidArgument("GameModule(" + LoadedFromFile +
85  ") can't find "
86  "source file: " +
87  sources->GetLine(i));
88  } else {
89 
90  SourceFiles.push_back(codeFile);
91  }
92  }
93 
94  // Resolve export files
95  if(exports) {
96 
97  for(size_t i = 0; i < exports->GetLineCount(); ++i) {
98 
99  // Skip empty lines to allow commenting out things //
100  if(exports->GetLine(i).empty())
101  continue;
102 
103  const std::string codeFile =
104  ScriptModule::ResolvePathToScriptFile(exports->GetLine(i), moduleFilePath);
105 
106  if(codeFile.empty()) {
107  throw InvalidArgument("GameModule(" + LoadedFromFile +
108  ") can't find "
109  "exported file: " +
110  exports->GetLine(i));
111  } else {
112 
113  ExportFiles.push_back(codeFile);
114  }
115  }
116  }
117 
118  // Read properties //
119  if(auto* data = properties->GetVariables().GetValueDirectRaw("ExtraAccess");
120  data != nullptr) {
121 
122  std::string flags;
123  if(data->GetVariableCount() != 1 ||
124  !data->GetValue().ConvertAndAssingToVariable(flags)) {
125 
126  throw InvalidArgument(
127  "GameModule(" + LoadedFromFile +
128  ") has an invalid value for property 'ExtraAccess': not string");
129  }
130 
131  try {
132  ExtraAccess = ParseScriptAccess(flags);
133  } catch(const InvalidArgument& e) {
134  throw InvalidArgument(
135  "GameModule(" + LoadedFromFile +
136  ") has an invalid value for property 'ExtraAccess': " + e.what());
137  }
138  }
139 
140  // Import modules just need to be copied
141  if(imports) {
142 
143  for(size_t i = 0; i < imports->GetLineCount(); ++i) {
144  if(imports->GetLine(i).empty())
145  continue;
146 
147  ImportModules.push_back(imports->GetLine(i));
148  }
149  }
150 
151  LEVIATHAN_ASSERT(!SourceFiles.empty(), "GameModule: empty source files");
152 }
153 
155 {
156  LoadedImportedModules.clear();
157 
158  ReleaseScript();
159 
160  UnRegisterAllEvents();
161 
162  Loader->GameModuleReportDestruction(*this);
163 
164  // // This would be a bit inconvenient with the new way modules are kept loaded
165  // if(Scripting) {
166 
167  // LOG_FATAL("GameModule: 'ReleaseScript' not called before destructor");
168  // }
169 }
170 // ------------------------------------ //
172 {
173  IsCurrentlyInitializing = true;
174 
175  // Resolve imports first
176  if(!ImportModules.empty()) {
177 
178  const auto desc = GetDescription(true);
179 
180  for(const auto& import : ImportModules) {
181 
182  // Load the dependency module. We give our full description to make debugging the
183  // chain easier
184  try {
185  auto imported = Loader->Load(import, desc.c_str());
186 
187  LoadedImportedModules.push_back(imported);
188  } catch(const NotFound& e) {
189 
190  LOG_ERROR("GameModule: Init: module \"" + import +
191  "\" could not be loaded by: " + desc + ", exception:");
192  e.PrintToLog();
193  return false;
194  }
195  }
196  }
197 
198  // Compile a new module //
199  ScriptModule* mod = NULL;
200 
201  LEVIATHAN_ASSERT(!Scripting, "GameModule may not already have Script instance created");
202 
203  Scripting =
204  std::shared_ptr<ScriptScript>(new ScriptScript(ScriptExecutor::Get()->CreateNewModule(
205  "GameModule(" + Name + ") ScriptModule", LoadedFromFile)));
206 
207  // Get the newly created module //
208  mod = Scripting->GetModule();
209 
210  for(const auto& file : SourceFiles) {
211  mod->AddScriptSegmentFromFile(file);
212  }
213 
214  // Also add imported files
215  for(const auto& import : LoadedImportedModules) {
216  const auto& fileList = import->GetExportFiles();
217 
218  if(fileList.empty()) {
219 
220  LOG_WARNING("GameModule: Init: imported module \"" + import->GetName() +
221  "\" doesn't specify any exports");
222  continue;
223  }
224 
225  for(const auto& file : import->GetExportFiles()) {
226  mod->AddScriptSegmentFromFile(file);
227  }
228  }
229 
230  // Set access flags //
231  if(ExtraAccess != 0)
232  mod->AddAccessRight(ExtraAccess);
233 
234  // Build the module (by creating a callback list) //
235  std::vector<std::shared_ptr<ValidListenerData>> containedlisteners;
236 
237  mod->GetListOfListeners(containedlisteners);
238 
239  if(mod->GetModule() == nullptr) {
240  // Fail to build //
241  Logger::Get()->Error("GameModule: Init: failed to build AngelScript module");
242  return false;
243  }
244 
245  RegisterStandardScriptEvents(containedlisteners);
246 
247  for(size_t i = 0; i < containedlisteners.size(); i++) {
248  // Bind generic event //
249  if(containedlisteners[i]->GenericTypeName &&
250  containedlisteners[i]->GenericTypeName->size() > 0) {
251 
252  // Registered in RegisterStandardScriptEvents
253  continue;
254  }
255 
256  // Skip global events
257  EVENT_TYPE etype = ResolveStringToType(*containedlisteners[i]->ListenerName);
258 
259  // Skip types that we handle ourselves //
260  if(etype == EVENT_TYPE_ERROR)
261  etype = GetCommonEventType(*containedlisteners[i]->ListenerName);
262 
263  if(etype != EVENT_TYPE_ERROR) {
264 
265  continue;
266  }
267 
268  Logger::Get()->Warning("GameModule: unknown event type " +
269  *containedlisteners[i]->ListenerName +
270  ", did you intent to use Generic type?");
271  }
272 
273  // Call init callbacks //
274 
275  // fire an event //
276  Event* initEvent = new Event(EVENT_TYPE_INIT, nullptr);
277  OnEvent(initEvent);
278  // Release our initial reference
279  initEvent->Release();
280 
281  IsCurrentlyInitializing = false;
282  return true;
283 }
284 
286 {
287  if(Scripting) {
288  // Call release callback and destroy everything //
289  // fire an event //
290  Event* releaseEvent = new Event(EVENT_TYPE_RELEASE, nullptr);
291  OnEvent(releaseEvent);
292  // Release our initial reference
293  releaseEvent->Release();
294 
295  // Remove our reference //
296  int tmpid = Scripting->GetModule()->GetID();
297  Scripting.reset();
298 
300  }
301 }
302 // ------------------------------------ //
303 DLLEXPORT std::string GameModule::GetDescription(bool full /*= false*/)
304 {
305  return "GameModule(" + Name + (full ? " v" + Version + ") " : ") ") +
306  " owned by: " + OwnerName +
307  (full ? ", loaded from file: " + LoadedFromFile + "." : ".");
308 }
309 // ------------------------------------ //
311  ScriptRunningSetup& sargs, Event* event, GenericEvent* event2)
312 {
313  if(event)
314  return ScriptExecutor::Get()->RunScript<int>(
315  Scripting->GetModuleSafe(), sargs, this, event);
316  else
317  return ScriptExecutor::Get()->RunScript<int>(
318  Scripting->GetModuleSafe(), sargs, this, event2);
319 }
Interface for object file lists to implement.
Definition: ObjectFile.h:21
DLLEXPORT asIScriptModule * GetModule(Lock &guard)
Builds the script if applicable.
ScriptRunResult< ReturnT > RunScript(const std::shared_ptr< ScriptModule > &module, ScriptRunningSetup &parameters, Args &&... args)
Runs a function in a script.
Class that represents a statically defined event.
Definition: Event.h:106
virtual DLLEXPORT size_t GetLineCount() const =0
Returns the number of text lines.
DLLEXPORT GameModule::pointer Load(const std::string &modulename, const char *requiredby)
Returns a module by name.
#define LOG_ERROR(x)
Definition: Define.h:92
Definition: lz4.c:260
EVENT_TYPE
Engine events that are triggered at certain times.
Definition: Event.h:12
DLLEXPORT std::string GetDescription(bool full)
Definition: GameModule.cpp:303
static const StringTypeN GetPath(const StringTypeN &filepath)
Returns the path part of a path+filename.
void GameModuleReportDestruction(GameModule &module)
Used by GameModule to report that it is going to be deleted.
static DLLEXPORT std::unique_ptr< ObjectFile > ProcessObjectFile(const std::string &file, LErrorReporter *reporterror)
Reads an ObjectFile to an in-memory data structure.
FORCE_INLINE void Release() const
removes a reference and deletes the object if reference count reaches zero
Interface for object file text blocks to implement.
Definition: ObjectFile.h:71
#define LOG_WARNING(x)
Definition: Define.h:91
Holds a result of the new script run method.
static DLLEXPORT ScriptExecutor * Get()
DLLEXPORT bool DeleteModuleIfNoExternalReferences(int ID)
virtual ScriptRunResult< int > _DoCallWithParams(ScriptRunningSetup &sargs, Event *event, GenericEvent *event2) override
Definition: GameModule.cpp:310
DLLEXPORT void ReleaseScript()
Releases the script.
Definition: GameModule.cpp:285
Class that represents a dynamically defined event.
Definition: Event.h:140
virtual DLLEXPORT const std::string & GetLine(size_t index) const =0
Gets a line from index \except ExceptionInvalidArgument when the index is out of bounds.
DLLEXPORT const char * what() const noexcept override
Definition: Exceptions.cpp:29
DLLEXPORT void Warning(const std::string &data) override
Definition: Logger.cpp:190
DLLEXPORT void GetListOfListeners(std::vector< std::shared_ptr< ValidListenerData >> &receiver)
#define LEVIATHAN_ASSERT(x, msg)
Definition: Define.h:100
DLLEXPORT bool Init()
Makes the scripts usable.
Definition: GameModule.cpp:171
static DLLEXPORT std::string ResolvePathToScriptFile(const std::string &inputfilename, const std::string &relativepath, bool checkworkdirrelative=true)
Finds a path to source file or returns an empty string.
static DLLEXPORT Logger * Get()
Definition: Logger.cpp:106
virtual DLLEXPORT NamedVars & GetVariables()=0
Gets a reference to the underlying variables.
DLLEXPORT GameModule(const std::string &filepath, const std::string &ownername, GameModuleLoader *loadedthrough)
Definition: GameModule.cpp:13
DLLEXPORT NamedVariableList * GetValueDirectRaw(const std::string &name) const
Definition: NamedVars.cpp:1096
#define DLLEXPORT
Definition: Include.h:84
DLLEXPORT AccessFlags ParseScriptAccess(std::string_view flagstring)
Parses a string for access flags.
Definition: AccessMask.cpp:7
DLLEXPORT ~GameModule()
Definition: GameModule.cpp:154
The access mask controls which registered functions and classes a script sees.
Definition: GameModule.h:12
DLLEXPORT void Error(const std::string &data) override
Definition: Logger.cpp:177
virtual DLLEXPORT void PrintToLog() const noexcept
Definition: Exceptions.cpp:35
DLLEXPORT void AddAccessRight(AccessFlags newaccess)
Adds flags to the AccessMask.
Definition: ScriptModule.h:81
Manages loading GameModule objects to make sure that each is loaded only once.
DLLEXPORT bool AddScriptSegmentFromFile(const std::string &file)
Adds an entire file as a script segment.