Skip to content
Lua code for FAF
Branch: develop
Clone or download
Latest commit 5e80b88 Jan 13, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
effects
engine Finished patch II Sep 14, 2018
env add new textures, decals and props Jun 11, 2018
etc/faf
loc Add FR LOC for ladder feedback tooltip Dec 26, 2018
lua update changelog 3703 Jan 13, 2019
meshes/game Normalize line endings to LF Dec 20, 2014
projectiles HARMS fix Sep 22, 2018
props/DefaultWreckage
schook/lua Fix Notify overlay problems in replays Nov 27, 2017
testmaps cleanup brackets and spaces May 6, 2017
textures Weapon priorities Dec 23, 2018
units Change Target Bone Jan 11, 2019
.gitattributes Normalize line endings to LF Dec 20, 2014
.gitignore Ignore .DS_Store Aug 19, 2015
.travis.yml Simple lua syntax checker using Travis CI Oct 26, 2014
CONTRIBUTING.md Split contribution guidelines from readme Jan 23, 2016
PULL_REQUEST_TEMPLATE.md Fix branch link in template Jun 16, 2017
README.md Trigger deployment to 2nd test server Oct 14, 2018
SupComDataPath.lua Adding SupComDataPath.lua and init.lua files. Oct 16, 2014
branchmodel.png
changelog.md update changelog 3703 Jan 13, 2019
init.lua load props folder in the init file May 26, 2018
mod_info.lua update changelog 3703 Jan 13, 2019
patch_info.json Add patch_info.json to FA repo May 6, 2017
run_lua_tests.sh

README.md

FAF LUA Code

master develop
Build Status Build Status

Current patch is: 3656

Changelog can be found here.

Contributing

See guidelines for contributing here.

See git branch model for the repository and how it relates to FAF client game modes here.

Running the game with your changes

When FA starts without any command line arguments, it looks for a file called SupComDataPath.lua.

This file is a normal lua-file, that is allowed to use FA's IO operations to load directories and compressed directories (zip files) into the virtual file system.

The normal file looks like this:

mount_contents(SHGetFolderPath('PERSONAL') .. 'My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\mods', '/mods')
mount_contents(SHGetFolderPath('PERSONAL') .. 'My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\maps', '/maps')
mount_dir(InitFileDir .. '\\..\\gamedata\\*.scd', '/')
mount_dir(InitFileDir .. '\\..', '/')

Where mount_contents is a helper function defined also in that file.

This loads all maps and mods in your ~\Documents\My Games\... folder, followed by the core game files that are located in compressed .scd files.

What's important to note about the load order is that if two directories contain the same file, the first loaded takes precedence. There are ways to get around this using hooks, that I'll explain in the end.

FAF extends the loading mechanism of FA, by using different initialization files: One for each featured mod.

init_faf.lua contains a whitelist of files that it allows to be loaded, this whitelist is implemented using the function mount_dir_with_whitelist, which is just like the helper function from the normal FA init file, except for the whitelist which only allows the given named files to be loaded.

The actual loading in init_faf.lua is done here:

-- these are the classic supcom directories. They don't work with accents or other foreign characters in usernames
mount_contents(SHGetFolderPath('PERSONAL') .. 'My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\mods', '/mods')
mount_contents(SHGetFolderPath('PERSONAL') .. 'My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\maps', '/maps')
-- these are the local FAF directories. The My Games ones are only there for people with usernames that don't work in the upper ones.
mount_contents(InitFileDir .. '\\..\\user\\My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\mods', '/mods')
mount_contents(InitFileDir .. '\\..\\user\\My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\maps', '/maps')
mount_dir_with_whitelist(InitFileDir .. '\\..\\gamedata\\', '*.nxt', '/')
mount_dir_with_whitelist(InitFileDir .. '\\..\\gamedata\\', '*.nx2', '/')
-- these are using the newly generated path from the dofile() statement at the beginning of this script
mount_dir_with_whitelist(fa_path .. '\\gamedata\\', '*.scd', '/')
mount_dir(fa_path, '/')[/code]

After adding all maps and mods to the search path, all .nxt compressed directories are loaded (as filtered by the whitelist). This currently includes: murderparty, labwars, avanced strategic icons and texturepack. They are loaded in alphabetical order.

Followed by .nxt files, .nx2 files are loaded. These comprise compressed directories for each subdirectory of the FA virtual file system: effects, env, loc, lua, modules, schook, projectiles, units, textures and meshes.

After all FAF-files have been loaded, the init file loads the base-game .scd files. Since these are loaded last, files that are in the FAF-directories take precedence and shadow the base game files.

Hooking

Hooking with the FA virtual file system simply means concatenating files.

Given the following directories and load-order:

cool_mod directory containing:

  • /hook/lua/file.lua

FAF.scd containing:

  • /lua/file.lua
  • /schook/lua/file.lua

FA.scd containing:

  • /lua/file.lua

What ends up in the actual filesystem used by FA is:

/lua/file.lua = FAF.scd/lua/file.lua + cool_mod/hook/lua/file.lua + FAF.scd/schook/lua/file.lua

Where "fileA + fileB" means that fileB has been appended to fileA.

The directory that is used for hooks can be configured in the init.lua file, and it customizable for each mod in the mod_info.lua file.

Setting up a development init file

ForgedAlliance.exe takes several useful command-line arguments, and it's even possible to make your own. An article on this can be found here.

We can use a custom init file to ease the development process. The following file init file can be used:

dev_path = 'C:\\Workspace\\forged-alliance-forever-lua'
-- this imports a path file that is written by Forged Alliance Forever right before it starts the game.
dofile(InitFileDir .. '\\..\\fa_path.lua')
path = {}
local function mount_dir(dir, mountpoint)
    table.insert(path, { dir = dir, mountpoint = mountpoint } )
end
local function mount_contents(dir, mountpoint)
    LOG('checking ' .. dir)
    for _,entry in io.dir(dir .. '\\*') do
        if entry != '.' and entry != '..' then
            local mp = string.lower(entry)
            mp = string.gsub(mp, '[.]scd$', '')
            mp = string.gsub(mp, '[.]zip$', '')
            mount_dir(dir .. '\\' .. entry, mountpoint .. '/' .. mp)
        end
    end
end
-- these are the classic supcom directories. They don't work with accents or other foreign characters in usernames
mount_contents(SHGetFolderPath('PERSONAL') .. 'My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\mods', '/mods')
mount_contents(SHGetFolderPath('PERSONAL') .. 'My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\maps', '/maps')
-- these are the local FAF directories. The My Games ones are only there for people with usernames that don't work in the uppder ones.
mount_contents(InitFileDir .. '\\..\\user\\My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\mods', '/mods')
mount_contents(InitFileDir .. '\\..\\user\\My Games\\Gas Powered Games\\Supreme Commander Forged Alliance\\maps', '/maps')
mount_dir(dev_path, '/')
-- these are using the newly generated path from the dofile() statement at the beginning of this script
mount_dir(fa_path .. '\\gamedata\\*.scd', '/')
mount_dir(fa_path, '/')
hook = {
    '/schook'
}
protocols = {
    'http',
    'https',
    'mailto',
    'ventrilo',
    'teamspeak',
    'daap',
    'im',
}

At the very top there is the line: dev_path, which should be set to where ever you have cloned this repository.

Starting Forged Alliance from the commandline with the following arguments:

ForgedAlliance.exe /init "init_dev.lua" /EnableDiskWatch /showlog

Will put it into a mode where it will look for updates to files that it has loaded. So when you modify a unit file or a blueprint, the game will re-load the file and put it into the active session.

This way, you don't need to restart the game every time you make a change, you simply need to make a new unit of the type, spawn a new projectile or do whatever it is you're doing.

It's not perfect; some changes will require a full game restart, and certain changes can cause crashes. But it's a lot better than reloading the game for every change, every time.

You can’t perform that action at this time.