Howto:Creating a simple modding framework

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.
Screenshot showing a simple directory structure for modding FlightGear, following existing fgdata conventions

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

1rightarrow.png 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
  1. Bomber  (May 29th, 2014).  Re: Does FlightGear has Multiplayer Combat mode? .
  2. Hooray  (Nov 22nd, 2014).  Re: How to add sort of addon? .
  3. Hooray  (Aug 17th, 2013).  Re: File Path in different Operating systems .
  4. Hooray  (Jan 2nd, 2016).  Re: Military simulation (from Su-15 Screenshots) .
  5. Hooray  (Jan 3rd, 2016).  Re: Military simulation (from Su-15 Screenshots) .
  6. Hooray  (Nov 22nd, 2014).  Re: How to add sort of addon? .
  7. Hooray  (Nov 22nd, 2014).  Re: How to add sort of addon? .