Howto:Creating a simple modding framework
This article is a stub. You can help the wiki by expanding it. |
Status
Motivation
There are examples out there where games/sims have huge modding communities, harnessing the knowledge and dedication of these people to great effect... But the problem here is that it means 'letting go' and it seems to me that it's more fun to have yet another coding language to learn, another empire of kudos to build than it is for Flightgear to succeed.[1]
There still is no dedicated "modding" or "addon" framework - however, all required building blocks are definitely there.
Usually, people will just use what works for them, without coming up with a framework for extending the simulator in a seamless fashion.[2]
Not even stuff like Bombable or the local weather system should really touch $FG_ROOT
the way they are - ideally, we'd have a standard "modding" infrastructure to allow users to extend the simulator without messing with existing files and folders. Personally, I'd rather add a new folder than add stuff to places where it doesn't strictly belong - imagine for a second your stuff were to be merged upstream, and there'd be a code review.
I don't quite agree with my own way of modifying fgdata for the same reasons - we should really establish some standardied way to add new addons/plugins that can be easily installed, customized and uninstalled - at least in XML/Nasal space, that'd be pretty straightforward.[3]
FlightGear hasn't been designed, developed and maintained with "moddability" in mind, i.e. should we continue to see hugely different standpoints among active contributors, it would make sense to reconsider that, given that "modding" is almost certainly the option that is to be preferred for the sake of the project instead of seeing it possibly forked by those whose ideas/contributions are not that well received by the core of active contributors/developers.[4]
Having combat addons/functionality as a distributed part of FG is different to supporting the underlying building blocks to allow such things to be developed and maintained independently, i.e. without a focus on combat specifically - just like xml, the property tree, effects, shaders, FDMs, Nasal, Canvas etc don't specifically support the high-level use-case they're used for, e.g. simulating airliners etc
If the main question is one of deployment, the package manager is going to solve that over time - and the number of useful changes to allow more/easier modding is not that large- such an external hangar with combat-related addons could then be hosted by folks like FGUK or T4T.[5]
Implementation
We could have a loader.nas
script in $FG_ROOT/Nasal
that can load XML-based addons[6]
foreach(var file; files)
io.load_nasal(file);
That can be trivially done with XML files, too - by using io.read_properties()
.[7]
Objective
List of addons
tanker.nas
- tutorials
- fgcamera
- advanced weather
- bombable
Background
Directory Structure
Mods should be using their own directory structure, resembling $FG_ROOT
(the base package), e.g.:
- Aircraft
- GUI
- Textures
- Sounds
- Nasal
etc
Requirements
- register hooks to invoke callbacks for specific simulator/addon events (pause/unpause etc)
- mutual dependencies
manifest.xml
<?xml version="1.0"?>
<PropertyList>
<addon>
<name></name>
<description></description>
<version></version>
<changelog></changelog>
</addon>
</PropertyList>
<!-- end of manifest.xml -->
Versioning
See FlightGear Version Check for the main article about this subject. |
Interface
- loadScript()
- loadModel()
- loadTexture()
- ...
Prototype
The prototype will be set up like this:
- an optional Nasal submodule stored in
$FG_ROOT/Nasal/modding
- mods live under $FG_ROOT/Mods, each in its own folder
- each mod folder's structure will basically resemble the structure of the base package, with its sub-folders for resources like scripts, textures, sounds etc
- each mod will have a top-level
mod.specs
file - each
mod.specs
file is a conventional Nasal file that inherits from an interface class living in the Mod namespace - the
mod.specs
file implements routines for enabling/disabling and configuring the mod
The loader script will traverse $FG_ROOT/Mods
to obtain a list (vector) of directories.
For each directory found, it will use io.load_nasal()
to call the mod.specs
file for that mod
In turn, each mod.specs
file will register itself, e.g. to add menubar items and register GUI dialogs etc
Proof of Concept
The following snippet of code assumes that you put it into $FG_ROOT/Nasal/mod/mod.nas
In addition, you need to add a folder to $FG_ROOT
named Mods, beneath this folder you can add other folders (one, two, three in this case) - with each folder being treated as a separate "mod", having its own mod.specs
file
diff --git a/Mods/one/mod.specs b/Mods/one/mod.specs
new file mode 100644
index 0000000..c1b3d8f
--- /dev/null
+++ b/Mods/one/mod.specs
@@ -0,0 +1 @@
+print("Mod 1 registered");
diff --git a/Mods/three/mod.specs b/Mods/three/mod.specs
new file mode 100644
index 0000000..c1b3d8f
--- /dev/null
+++ b/Mods/three/mod.specs
@@ -0,0 +1 @@
+print("Mod 3 registered");
diff --git a/Mods/two/mod.specs b/Mods/two/mod.specs
new file mode 100644
index 0000000..c1b3d8f
--- /dev/null
+++ b/Mods/two/mod.specs
@@ -0,0 +1 @@
+print("Mod 2 registered");
##
# $FG_ROOT/Nasal/mod/loader.nas
var ModdingRegistry = {};
var ModdingInterface = {
new: func() {},
del: func() {},
register: func(name) {
var slotNotEmpty = contains(ModdingRegistry, name);
if (slotNotEmpty) {
print("Warning: Overwriting/re-adding mod:", name);
ModdingRegistry[name].del(); # invoke the destructor of the mod
# ModdingRegistry[name] =
}
},
};
var fgroot = getprop("/sim/fg-root");
var modFolder = fgroot ~ '/'~ 'Mods/';
# http://wiki.flightgear.org/Nasal_library#directory.28.29
var modDirectories = directory( modFolder );
# debug.dump( modDirectories );
foreach(var mod; modDirectories) {
print("Mod directory found:", mod);
var entrypoint = modFolder ~ mod ~ '/mod.specs';
print("Trying to load:", entrypoint,"\n");
io.load_nasal( entrypoint );
}
## invoke:
# init code
# setup code
# loading code
# clean up code
References
|