Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

xModManager is a simple Roblox tool designed to make modding for games much simpler.
Under the hood, xModManager consists of a VFS (Virtual File System) and a basic VPM (Virtual Process Manager).
The modding programming language is Lua. Note it’s NOT Luau and lacks some features.

At a simple level, Mods are stored in the VFS as strings with a filename at a specific path. The process manager then reads the Lua code from there and returns an executable that is stored in the process table. When the mod is run, that executable is spawned as a new task with task.spawn() that doesn’t block execution of your current script.

Why use xModManager?

1. Simplicity

xModManager is very simple to use, and exposes a few easy-to-understand APIs. It’s codebase is very readable.

2. Error handling

xModManager returns an error code alongside the return values for almost every function. An error code of 0 means success, while any other return code is an error of some type.

3. Security

It may sound counterintutive that mods that can be written as Lua strings are secure, but they are!
First off, the Lua code doesn’t have access, by default, to any environemnt variable that can cause an escape from the environment.
Second off, if an user adds accidentally an environment variable that could cause an escape, xModManager warns in the console.

4. Easy manipulation of mods

A mod process can easily be killed with the SIGKILL signal.
You can signal to a mod using SIGCOM, and that mod can attach to that using a function provided in the environment and using a callback.

5. Configurability

There is a module named Configuration which is very configurable and has a lot of settings that affect how the mod manager works.

The structure of xModManager

xModManager comes with the following hierarchy

xModManager
├── Configuration
├── ModFileSystem
├── Signals
└── ModProcessManager
    └── vLua
        ├── FiOne
        └── Yueliang

xModManager

The root module of xModManager, requires all modules and returns them in a table, so one require can be done in your file instead of four.

Configuration

The settings of xModManager

ModFileSystem

The VFS component of the mod manager

Signals

Contains signals that correspond to a specific number. This is so you don’t have to write signals in numbers, but you can read them and memorize them.

ModProcessManager

The VPM component of the mod manager

vLua, FiOne and Yueliang

The bytecode compiler and interpreter of Lua that ModProcessManager uses behind the hood. You, the user do not need to care about these modules.

Using xModManager

This section will teach you how to use the mod manager at a very basic scale.
You should read the API section as well to understand how to use xModManager at its fullest

Information

Naming

There will be a few names that you may not be familiar with in this article, and I will explain as many of them over here

PID

PID stands for Process ID, and it is a unique identifier that is assigned to each process in xModManager

Environment

The environment is all of the external functions the Lua code gets. In Roblox, for example, your Lua code gets in its environment functions and tables like

Instance
UDim2
Vector3

These are part of the Luau environment and you can use them to do various actions across the Roblox engine.

VFS

VFS stands for Virtual File System. In xModManager, the VFS plays a crucial role by assigning each mod file a path and some attributes. In addition, it also provides a lot of additional features, such as creating directories, removing and listing.

PM

PM stands for Process Manager. In xModManager, the process manager saves all mod processes in a table, their identifier being their PID. It contains useful functions and information.

Terms used interchangeably

There will be a few terms that will be used interchangeably

Mod and File

In the file system, a mod is represented as a file entry. Therefore APIs that operate on mods return FileHandle objects.

Creating our first mod

Let’s create our first mod!
This mod will just print “Hello, World!” to the console.
The first step to use xModManager, is to… require it!

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager

Next off, let’s write to the virtual file system our Lua code!

local return_code, _ = FileSystem.WriteMod(
    "Mods/cool_mod_name.mod",   -- the file path

    [[
    print("Hello, world!")
    ]]                          -- the code
)

-- We check if return code is success
if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

We have successfully written our mod to disk if there is no warning in the console! It is now time to spawn our mod as a process

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/cool_mod_name.mod",   -- we pass the mod path
    {}, -- we can leave args as an empty table for now
    {} -- we can leave extra_env as an empty table for now
)

-- We do the same here as we did for the file system
if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

Check out Process Manager to understand what the functions and empty tables are doing exactly
You may notice the mod is not executing. That is because we just registered it, we still have to execute it!

ProcessManager.RunMod(pid)

Together, the code looks like this:

local xModManager = require(path_to_module)
local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager

local return_code, _ = FileSystem.WriteMod(
    "Mods/cool_mod_name.mod",   -- the file path

    [[
    print("Hello, world!")
    ]]                          -- the code
)

-- We check if return code is success
if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/cool_mod_name.mod",   -- we pass the mod path
    {}, -- we can leave args as an empty table for now
    {} -- we can leave extra_env as an empty table for now
)

-- We do the same here as we did for the file system
if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

If you run this, you should now see “Hello, World!” in the console!
Good job, you just wrote your first mod!

Using arguments in our mod

What about arguments? Let’s learn how to use them.
This mod will print all the arguments passed to it.
First, let’s require all our needed modules

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager

Let’s write a new mod that reads all the args and prints them

local return_code, _ = FileSystem.WriteMod(
    "Mods/even_cooler_mod.lua",   -- the file path

    [[
    for i, v in ipairs(args()) do
        print(v)
    end
    ]]                          -- the code
)

-- We check if return code is success
if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

Now let’s spawn and run our mod.

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/even_cooler_mod.lua", -- we pass the mod path
    {"hello", "world", "coolest argument"}, -- the args
    {} -- leave empty for now
)

-- Check return code
if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

The full code:

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager

local return_code, _ = FileSystem.WriteMod(
    "Mods/even_cooler_mod.lua",   -- the file path

    [[
    for i, v in ipairs(args()) do
        print(v)
    end
    ]]                          -- the code
)

-- We check if return code is success
if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/even_cooler_mod.lua", -- we pass the mod path
    {"hello", "world", "coolest argument"}, -- the args
    {} -- leave empty for now
)

-- Check return code
if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

You should see in the console:

hello
world
coolest argument

If so, then you have just built your second mod!

Using a custom environment

Mods execute in a sandbox. They cannot automatically access your game’s functions or objects. Instead, you explicitly decide what a mod is allowed to use by passing an environment when spawning it.
We will give in the environment a function that simply creates a frame on the screen with the specified size (offset)
As always, first let’s require the needed modules

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager

Let’s also get the Player and PlayerGui

local Player = game:GetService("Players").LocalPlayer
local PlayerGui = Player:WaitForChild("PlayerGui")

Now, let’s create a ScreenGui inside of PlayerGui

local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Parent = PlayerGui

We now make the cool function, the one which creates a frame and parents it to the ScreenGui

local function createFrame(size_x, size_y)
    local frame = Instance.new("Frame")
    frame.Size = UDim2.fromOffset(size_x, size_y)
    frame.Position = UDim2.fromScale(0, 0)

    frame.BackgroundTransparency = 0
    
    frame.Parent = ScreenGui
end

Now, we will create a mod

-- NOTICE: we use the CreateFrame function
local return_code, _ = FileSystem.WriteMod(
    "Mods/cool_mod_name.mod",   -- the file path

    [[
    CreateFrame(100, 100)
    ]]
)

-- We check if return code is success
if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/cool_mod_name.mod",   -- we pass the mod path
    {},
    {CreateFrame = createFrame} -- we now pass the createFrame function with the name CreateFrame
)

-- We do the same here as we did for the file system
if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

Together, it looks like:

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager

local Player = game:GetService("Players").LocalPlayer
local PlayerGui = Player:WaitForChild("PlayerGui")

local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Parent = PlayerGui

local function createFrame(size_x, size_y)
    local frame = Instance.new("Frame")
    frame.Size = UDim2.fromOffset(size_x, size_y)
    frame.Position = UDim2.fromScale(0, 0)

    frame.BackgroundTransparency = 0
    
    frame.Parent = ScreenGui
end

-- NOTICE: we use the CreateFrame function
local return_code, _ = FileSystem.WriteMod(
    "Mods/cool_mod_name.mod",   -- the file path

    [[
    CreateFrame(100, 100)
    ]]
)

-- We check if return code is success
if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/cool_mod_name.mod",   -- we pass the mod path
    {},
    {CreateFrame = createFrame} -- we now pass the createFrame function with the name CreateFrame
)

-- We do the same here as we did for the file system
if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

You should see a frame on the screen! If so, then we have successfully passed a function in the environment.

Communicating with a program using SIGCOM

Warning

This page assumes that Configuration.Allow_SIGCOM = true, Configuration.SIGCOM_Name = "on_sigcom", Configuration.GiveModProcessInfo = true and Configuration.ProcessInfoName = "_process"

Let’s communicate with our mod using SIGCOM. First, we require everything that we need.

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager
local Signals = xModManager.Signals

Next, let’s create our mod

local return_code, _ = FileSystem.WriteMod(
    "Mods/cool_signals.mod",

    [[
    on_sigcom(function(msg)
        print(msg)
        _process.exit()
    end)

    while _process.should_run() do
        wait(0.5)
    end
    ]]
)

if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/cool_signals.mod",
    {},
    {}
)

if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

Let’s analyze the mod source

[[
on_sigcom(function(msg)
    print(msg)
    _process.exit()
end)

while _process.should_run() do
    wait(0.5)
end
]]

As you can see, on SIGCOM, we register a function that receives the message, and all it does is print the message, then exit the process.
Finally, let’s wait 1 second, then signal SIGCOM

task.wait(1)
ProcessManager.Signal(Signals.SIGCOM, pid, "Hello, world!")

Together, it looks like this:

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager
local Signals = xModManager.Signals

local return_code, _ = FileSystem.WriteMod(
    "Mods/cool_signals.mod",

    [[
    on_sigcom(function(msg)
        print(msg)
        _process.exit()
    end)

    while _process.should_run() do
        wait(0.5)
    end
    ]]
)

if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/cool_signals.mod",
    {},
    {}
)

if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

task.wait(1)
ProcessManager.Signal(Signals.SIGCOM, pid, "Hello, world!")

You should see “Hello, world!” in the console. If so, congratulations!

Terminating a mod using SIGKILL

Terminating a mod is also easy using signals

SIGTERM

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager
local Signals = xModManager.Signals

local return_code, _ = FileSystem.WriteMod(
    "Mods/signals_test_1.mod",

    [[
    while _process.should_run() do
        print("Running")
        wait(0.5)
    end
    ]]
)

if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/signals_test_1.mod",
    {},
    {}
)

if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

task.wait(1)
ProcessManager.Signal(Signals.SIGTERM, pid)

The core line is:

ProcessManager.Signal(Signals.SIGTERM, pid)

This signal makes _process.should_run() return false, which will exit the loop.

SIGKILL

local xModManager = require(path_to_module)

local FileSystem = xModManager.FileSystem
local ProcessManager = xModManager.ProcessManager
local Signals = xModManager.Signals

local return_code, _ = FileSystem.WriteMod(
    "Mods/signals_test_2.mod",

    [[
    while true do end
    ]]
)

if return_code ~= FileSystem.ReturnCodes.WriteMod.Success then
    warn("An error happened!")
    return
end

local return_code, pid = ProcessManager.SpawnMod(
    "Mods/signals_test_2.mod",
    {},
    {}
)

if return_code ~= ProcessManager.ReturnCodes.SpawnMod.Success then
    warn("An error happened!")
    return
end

ProcessManager.RunMod(pid)

task.wait(1)
ProcessManager.Signal(Signals.SIGKILL, pid)

The important line here is:

ProcessManager.Signal(Signals.SIGKILL, pid)

SIGKILL terminates the program, without caring if the program cooperates or not.

API

Note

Did you read the information about all the names and definitions used in this article?

This section covers and talks about all of the APIs of xModManager in depth.

Configuration

Requiring

local Configuration = require(path_to_xModManager).Configuration

Items

NameDescription
pmThe process manager instance that is initialized inside of ModProcessManager
GiveModProcessInfoWhether mod should get process info
ProcessInfoNameThe name of the table from which mods can get their information
GiveModDefaultEnvironmentWhether mod should have the implicit environment or not
DefaultEnvironmentThe default environment the mod will get
Allow_SIGCOMWhether mod should be able to give a callback for the sigcom signal
SIGCOM_NameThe name of the sigcom function
AllowImportWhether mod should be able to import other modules
ImportAncestorsThe import parents the mod will try importing from
ImportDefaultTargetsDefault targets the mod can import without needing the file system
ImportNameThe name of the import function
ExcludedInSafetyCheckNames of fields in the environment that will be excluded in safety check

pm

  • Type: table
  • This will be initialized by ModProcessManager

GiveModProcessInfo

  • Type: boolean
  • Whether mod gets process info using the process info table (the name of that table is defined in ProcessInfoName)
  • A mod can access process information such as the pid

Default value:

Configuration.GiveModProcessInfo = true

ProcessInfoName

  • Type: string
  • The name of the process info table that the script uses to access important program information
  • You can change the name of the table, so instead of “_process” it can be “ProcessInfo”

Default value:

Configuration.ProcessInfoName = "_process"

GiveModDefaultEnvironment

  • Type: boolean
  • Whether the mod gets the default environment passed or not

Default value:

Configuration.GiveModDefaultEnvironment = true

DefaultEnvironment

  • Type: table
  • The default environment the mod gets

Default value:

Configuration.DefaultEnvironment = {
	math = math,
	string = string,
	table = table,
	task = task,
	next = next,
	print = print,
	unpack = unpack,
	xpcall = xpcall,
	pcall = pcall,
	ipairs = ipairs,
	pairs = pairs,
	tostring = tostring,
	tonumber = tonumber,
	type = type,
	typeof = typeof,
	signals = Signals,
	Vector2	= Vector2,
	UDim = UDim,
	UDim2 = UDim2,
	Color3 = Color3,
}

Allow_SIGCOM

  • Type: boolean
  • Whether a process is allowed to register a callback which will be called on SIGCOM or not
  • SIGCOM is useful for IPC, as you can pass a message alongside it

Default value:

Configuration.Allow_SIGCOM = true

SIGCOM_Name

  • Type: string
  • The name of the function that will register a callback to sigcom

Default value:

Configuration.SIGCOM_Name = "on_sigcom"

AllowImport

  • Type: boolean
  • Whether imports should be allowed or not

Default value:

Configuration.AllowImport = false

ImportAncestors

  • Type: table
  • All of the paths in the VFS that the mod can import from
  • For example, if AllowImport has the path “Mods/Folder1”, and our mod tries to run import(“Folder2/file.mod”), the path of the import will be “Mods/Folder1/Folder2/file.mod”

Default value:

Configuration.ImportAncestors = {}

ImportDefaultTargets

  • Type: table
  • Similar to the environment table, but instead of the mod getting access to the target directly, it has to import it.

Default value:

Configuration.ImportDefaultTargets = {}

ImportName

  • Type: string
  • The name of the import function

Default value:

Configuration.ImportName = "import"

ExcludedInSafetyCheck

  • Type: table
  • If a table or an instance is passed in the environment, the process manager will warn about it. Excluding it will make it not warn for the specific value

Default value:

Configuration.ExcludedInSafetyCheck = {}
Configuration.ExcludedInSafetyCheck["math"] = true
Configuration.ExcludedInSafetyCheck["string"] = true
Configuration.ExcludedInSafetyCheck["table"] = true
Configuration.ExcludedInSafetyCheck["task"] = true
Configuration.ExcludedInSafetyCheck["next"] = true
Configuration.ExcludedInSafetyCheck["print"] = true
Configuration.ExcludedInSafetyCheck["unpack"] = true
Configuration.ExcludedInSafetyCheck["xpcall"] = true
Configuration.ExcludedInSafetyCheck["pcall"] = true
Configuration.ExcludedInSafetyCheck["ipairs"] = true
Configuration.ExcludedInSafetyCheck["pairs"] = true
Configuration.ExcludedInSafetyCheck["tostring"] = true
Configuration.ExcludedInSafetyCheck["tonumber"] = true
Configuration.ExcludedInSafetyCheck["type"] = true
Configuration.ExcludedInSafetyCheck["typeof"] = true
Configuration.ExcludedInSafetyCheck["signals"] = true
Configuration.ExcludedInSafetyCheck["Vector2"] = true
Configuration.ExcludedInSafetyCheck["UDim"] = true
Configuration.ExcludedInSafetyCheck["UDim2"] = true
Configuration.ExcludedInSafetyCheck["Color3"] = true

Signals

Warning

You are free to rename the fields in this module, as internally the checks are done with the numeric values. This could cause issues for readability.

Requiring

local Signals = require(path_to_xModManager).Signals

Items

NameValueDescription
SIGTERM1Sets the should_run field of a program to false
SIGKILL2Cancels a program thread and cleans up
SIGCOM3Communicates with a program; a message can be specified

SIGTERM

  • Type: number
  • When this signal is sent, the should_run field of a program will be set to false.

SIGKILL

  • Type: number
  • When this signal is sent, the thread of the program is cancelled, and the process will be cleaned up from memory.

SIGCOM

  • Type: number
  • This signal can be sent with a message (string)
  • When this signal is sent, if there is a callback registered, it will be called with the message as the argument.

FileSystem

Note

After reading the APIs from this page, make sure to look over the return codes so you can best understand how the APIs work

Overview

The FileSystem module provides a VFS used by xModManager to store directories and mod files.
Directories are represented by DirectoryHandle objects. Files (Mods) are represented by FileHandle objects.
All methods return a return code that should be checked before using the returned handle.

Requiring

local ModFileSystem = require(path_to_xModManager).FileSystem

Items

EntryType

NameValueDescription
Invalid0An invalid entry
Directory1A valid directory entry
File2A valid file entry

Invalid

  • Type: number
  • An invalid entry that shouldn’t be used

Directory

  • Type: number
  • A valid entry for a directory that can be used

File

  • Type: number
  • A valid entry for a file that can be used

ReturnCodes

Check out Return Codes for more information

Exported Types

NameDescription
DirectoryHandleA handle for a directory
FileHandleA handle for a file

DirectoryHandle

Fields:

Type: number
Path: string
  • The path of the directory
  • In format “PARENT/DIRECTORY”
Attributes: string
  • Can be anything
  • Used to give the directory additional information
Parent: string
  • The parent of the directory
Name: string
  • The name of the directory

Default value:

local entry: DirectoryHandle = {
	Type = ModFileSystem.EntryType.Directory,
	Path = "",
	Attributes = "",
	Parent = "",
	Name = "",
}

FileHandle

Fields:

Type: number
Path: string
  • The path of the file
  • In format “PARENT/FILE.EXTENSION”
Attributes: string
  • Can be anything
  • Used for giving the file additional information
Parent: string
  • The parent of the file
Data: string
  • The data inside of the file
  • This is most commonly the Lua code of the mods
Extension: string
  • The extension of the file
Name: string
  • The name of the file

Default value:

local entry: FileHandle = {
	Type = ModFileSystem.EntryType.File,
	Path = "",
	Attributes = "",
	Parent = "",
	Data = "",
	Extension = "",
	Name = "",
}

Methods

NameDescription
GetParentGet the parent from a path
GetNameGet name from path
GetExtensionGet extension from path
CreateDirectoryCreate a new directory in the file system
WriteModWrite a mod to the file system
RemoveDirectoryRemove a directory from the file system
RemoveModRemove a mod from the file system
ReadModEntryGet a copy of a mod entry in the file system
ReadDirectoryEntryGet a copy of a directory entry in the file system

GetParent

function ModFileSystem.GetParent(path: string): string

Arguments:

path: string
The path from which we should determine the parent

Returns:

parent: string
The parent determined from the path

Usage:

local path = "Mods/child.mod"
local parent = ModFileSystem.GetParent(path)
print(parent) -- "Mods"

GetName

function ModFileSystem.GetName(path: string): string

Arguments:

path: string
The path from which we should determine the name

Returns:

name: string
The name determined from the path

Usage:

local path = "Mods/file_name.lua"
local name = ModFileSystem.GetName(path)
print(name) -- "file_name.lua"

GetExtension

function ModFileSystem.GetExtension(path: string): string

Arguments:

path: string
The path OR name from which we should determine the extension

Returns:

extension: string
The extension determined from the string provided

Usage:

local filename = "coolname.exe"
local extension = ModFileSystem.GetExtension(filename)
print(extension) -- "exe"

CreateDirectory

function ModFileSystem.CreateDirectory(
	path: string,
	attributes: string?
): (number, DirectoryHandle)

Arguments:

path: string
The path of the directory that should be created

attributes: string? The optional attributes that should be added to the directory in the file system

Returns:

return_code: number The return code of the function

handle: DirectoryHandle The handle of the directory

Usage:

local return_code, dhandle = ModFileSystem.CreateDirectory(
    "Mods/CoolDirectory",
    "Some cool attribute"
)

if return_code == ModFileSystem.ReturnCodes.CreateDirectory.Success then
    -- our directory has been written successfully!
    print(dhandle.Attributes)   -- "Some cool attribute"
end

WriteMod

function ModFileSystem.WriteMod(
	path: string,
	data: string,
	attributes: string?
): (number, FileHandle)

Arguments:

path: string The path of the mod that should be created

data: string The data of the mod that should be written, commonly Lua source, but anything can be written

attributes: string? The optional attributes that should be added to the file in the file system

Returns:

return_code: number The return code of the function

handle: FileHandle The handle of the file

Usage:

local return_code, fhandle = ModFileSystem.WriteMod(
    "Mods/my_cool_mod.lua",

    [[
    print("The mod code")
    ]],

    -- attributes are optional
)

if return_code == ModFileSystem.ReturnCodes.WriteMod.Success then
    -- the file has been written successfully
    print(fhandle.Data) -- 'print("The mod code")'
end

RemoveDirectory

function ModFileSystem.RemoveDirectory(path: string): number

Arguments:

path: string The path of the directory that should be removed

Returns:

return_code: number The return code of the function

Usage:

-- create a directory "Mods/ToBeRemoved" using CreateDirectory

local return_code = ModFileSystem.RemoveDirectory("Mods/ToBeRemoved")

if return_code == ModFileSystem.ReturnCodes.RemoveDirectory.Success then
    -- we have successfully removed the directory
end

RemoveMod

function ModFileSystem.RemoveMod(path: string): number

Arguments:

path: string The path of the mod that should be removed

Returns:

return_code: number The return code of the function

Usage:

-- create a mod "Mods/mod.lua" using WriteMod

local return_code = ModFileSystem.RemoveMod("Mods/mod.lua")

if return_code == ModFileSystem.ReturnCodes.RemoveMod.Success then
    -- we have successfully removed the file
end

ReadModEntry

function ModFileSystem.ReadModEntry(path: string): (number, FileHandle)

Arguments:

path: string The path to the file which should be read

Returns:

return_code: number The return code of the function

fhandle: FileHandle The copy of the file handle

Usage:

-- discard the second argument so we can read ourselves
local return_code = ModFileSystem.WriteMod(
    "Mods/example.lua",

    [[
    this is example data
    ]],
)

-- if return code is not 0 then cancel the attempt
if return_code ~= ModFileSystem.ReturnCodes.WriteMod.Success then return end

return_code, fhandle = ModFileSystem.ReadModEntry("Mods/example.lua")

-- abort if read fails as well
if return_code ~= ModFileSystem.ReturnCodes.ReadModEntry.Success then return end

print(fhandle.Data) -- "this is example data"

ReadDirectoryEntry

function ModFileSystem.ReadDirectoryEntry(path: string): (
    number, 
    DirectoryHandle, 
    {string}
)

Arguments:

path: string The path to the directory which should be read

Returns:

return_code: number The return code of the function

dhandle: DirectoryHandle The copy of the directory handle

children: {string} The children of the directory

Usage:

-- create a directory "Mods/FinalTest" using CreateDirectory

local return_code, dhandle, _ = ModFileSystem.ReadDirectoryEntry(
    "Mods/FinalTest"
)

if return_code == ModFileSystem.ReturnCodes.ReadDirectoryEntry.Success then
    -- success
    print(dhandle.Name) -- "FinalTest"
end

Process Manager

Note

Also read:

Signals
Return Codes

Caution

The process manager does not reuse PIDs. This should be fine, as Lua numbers lose precision around 9 quadrillion.

Requiring

local ModProcessManager = require(path_to_xModManager).ProcessManager

Items

env

This item is just initialized at runtime with the default environment, or {} if a default environment is disabled

ReturnCodes

Check out Return Codes for more information

MasterHandler

The MasterHandler is the core component of the Process Manager which makes it a secure process manager. It is documented on another page and not this one.

Exported Types

NameDescription
ProcessHandleA handle to a process

ProcessHandle

Fields:

pid: number
  • The PID of the process
exec_path: string
  • The path at which the program started executing
cwd: string
  • The current working directory
  • This can be changed
running: boolean
  • Whether the program is running or not
should_run: boolean
  • Whether the program should run or not
args: {string}
  • All of the arguments passed to the program
  • Can be an empty table
proc_func: () -> any
  • The function of the process
  • Determined from the Lua code
sigcom_func: ((msg: string) -> any)?
  • The SIGCOM callback registered by the process
  • If not registered, this will be nil and won’t be called on SIGCOM

Warning

This field is initialized when the callback is registered, not during process creation.

thread: thread?
  • The thread that is handling the process
  • Spawned with task.spawn

Warning

This field is initialized once the process is run, not when it is created.

sigcom_thread: thread?
  • The thread that is handling SIGCOM
  • Spawned with task.spawn

Warning

This field is initialized every SIGCOM signal, and is then cancelled.
This can cause race conditions! Use with caution.

Methods

NameDescription
SpawnModSpawns a process from specified path
RunModRuns a process with the specified PID
SignalSends a signal to the process
ListProcessesLists all processes’ PID
IsRunningChecks if a process with specified PID is running or not

SpawnMod

function ModProcessManager.SpawnMod(
	path: string,
	args: {string},
	extra_env: {[string]: any}
): (number, number | string | nil)

Arguments:

path: string
The path to the lua source to be spawned

args: {string}
An array of arguments the program will use

extra_env: {[string]: any}
Additional environment for the process

Returns:

return_code: number
The return code of the function

pid: number | error_message: string | nil
If this function succeeds and the process is run, the return value will be - pid: number (the PID of the process)
If this function fails at compile time, the return value will be - error_message: string (the error message)
If this function fails at the file system level, the return value will be - nil

Usage:

local return_code, pid = ModProcessManager.SpawnMod(
    "Mods/path_to_your_mod.lua",
    {},
    {}
)

if return_code == ModProcessManager.ReturnCodes.SpawnMod.Success then
    -- we can now use the PID!
end

RunMod

function ModProcessManager.RunMod(pid: number): number

Arguments:

pid: number The PID of the spawned process to be executed

Returns:

return_code: number The return code of the function

Usage:

-- we first spawn a mod with SpawnMod()

local return_code, pid = ModProcessManager.SpawnMod(
    "Mods/path_to_your_mod.lua",
    {},
    {}
)

if return_code == ModProcessManager.ReturnCodes.SpawnMod.Success then
    return_code = ModProcessManager.RunMod(pid)

    if return_code == ModProcessManager.ReturnCodes.RunMod.Success then
        -- it is guaranteed that the mod is now running successfully
        -- this does NOT guarantee that errors won't happen at runtime.
    end
end

Signal

function ModProcessManager.Signal(signal: number, pid: number, msg: string?): nil

Arguments:

signal: number The signal which will be sent to the program

pid: number The PID of the target program

msg: string? In case of SIGCOM, the message to be passed to the program

Returns:

nil

Usage:

local Signals = require(path_to_xModManager).Signals
-- Spawn and run a mod using SpawnMod and RunMod

-- This will kill the process!
ModProcessManager.Signal(Signals.SIGKILL, process_pid)

ListProcesses

function ModProcessManager.ListProcesses(): {number}

Arguments:

nil

Returns:

processes: {number} An array of all currently running processes

Usage:

local processes = ModProcessManager.ListProcesses()

for i, v in ipairs(processes) do
    print(v) -- print the PID of each running process
end

IsRunning

function ModProcessManager.IsRunning(pid: number): boolean

Arguments:

pid: number The PID of the process which should be checked if running

Returns:

is_running: boolean Whether the process is running or not

Usage:

if ModProcessManager.IsRunning(6) then
    print("PID 6 is running!")
else
    print("PID 6 is not running!")
end

Master Handler

Return Codes

File System

CreateDirectory

NameValueDescription
Success0The directory has been created successfully
DirectoryAlreadyExists1Another directory at the same path exists
ParentNotFound2The parent of the target directory path doesn’t exist
InvalidParentType3The parent of the path is not a directory
InvalidPathCharacter4The path contains an invalid character

WriteMod

NameValueDescription
Success0The file has been created successfully
DirectoryWithSamePathExists1A directory at the same path already exists
ParentNotFound2The parent of the target file path doesn’t exist
InvalidParentType3The parent of the path is not a directory

RemoveDirectory

NameValueDescription
Success0The directory has been removed successfully
CanNotRemoveRoot1The root (“Mods”) folder can not be removed
DirectoryNotFound2The directory doesn’t exist
CanNotRemoveFiles3RemoveDirectory doesn’t work with files

RemoveMod

NameValueDescription
Success0The file has been removed sucessfully
FileNotFound1The file doesn’t exist
CanNotRemoveDirectories2RemoveMod doesn’t work with directories

ReadModEntry

NameValueDescription
Success0The file has been read successfully
FileNotFound1The file doesn’t exist
CanNotReadDirectory2ReadModEntry doesn’t work with directories

ReadDirectoryEntry

NameValueDescription
Success0The directory has been read successfully
DirectoryNotFound1The directory doesn’t exist
CanNotReadFile2ReadDirectoryEntry doesn’t work with files

Process Manager

SpawnMod

NameValueDescription
Success0The process has been spawned successfully
FileNotFound1The file was not found in the VFS
CanNotExecuteDirectory2The data at specified path was a directory
CompileError3An error happened at compile time

RunMod

NameValueDescription
Success0The process started running succesfully
PIDNotFound1The specified PID doesn’t exist
PIDAlreadyRunning2A process with the specified PID is already running