Mission system prototype


Infamous Developer
Jan 31, 2018
Prague, CZ
Hi everyone,
TheJesser on Discord gave me an idea of "addon missions" for existing terrains, a kind of new mod type which would add scenarios to existing terrains: https://discord.com/channels/136544456244461568/189904947649708032/1071847722128244916. After a quick chat, we made a rough design:
  • Introduce a new mod type, 'missionzip' - works the same as 'skinzip' (download from repository, regen cache, load using selector UI) except it would add a scenario to terrain.
  • Each missionzip would contain one or more '*.mission' files - these would be similar to TOBJ in syntax and could be also bundled directly in the terrain ZIP (same as .skin files can be directly in the vehicle ZIP)
  • To load a mission, load a terrain and go to Top Menubar, Simulation menu -> 'Load mission' which opens Selector UI with list of missions for that terrain.
  • To see list of loaded missions with progress info, Top Menubar will contain new menu 'Missions', similar to actor list.
And these are features I want the system to have:
  • Integration with our races system - a race is definitely a type of mission. The race script is pretty flexible so I can build on top of it.
  • No scripting knowledge required - you should only need scripting if you want it, but the simple scenarios should be just data-driven.
  • Extensibility via scripting - Everything possible via the mission system must also be possible via scripting, so that modders can create scenarios beyond what the .mission fileformat offers at any time. Ideally, the whole mission execution should be driven by a built-in script, with the possibility of modder overriding it.
  • Possibility to edit scenarios in-game using a script, similar to the road editor - This was requested on GitHub https://github.com/RigsOfRods/rigs-of-rods/issues/3011 and also makes complete sense. Given the "no scripting" requirement above, this means even the races need to get some definition file, because currently you need to define them directly in code (see the generator page CuriousMike kindly ported and documented: https://docs.rigsofrods.org/terrain-creation/race-generator/). Even if an editor script could read them from code, it couldn't save them back to code.
Finally this is how I imagine the system will work technically:
  • User places the .missionzip to mods directory, either by RepoUI download or by download from browser.
  • Modcache updates and enlists the missions.
  • When player wants to load mission, the SelectorUI will open in 'LT_Mission' mode using the 'guid:<the terrain GUID>' search method, listing only missions for current terrain. Like skinzips, missions won't work if terrain has no GUID.
  • Once mission is selected, game will execute a script to prepare it - either a script attached to the mission, or built-in script 'mission_default.as' if there is none. This is inspired by terrain scripts - if there are none defined in .terrn2 [Scripts] section, game executes 'default.as'.
  • The 'mission_default.as' script will use the `GenericDocument` API to load and read the .mission file, and then existing scripting (including the race system) to prepare the scenario.
  • From this point it's scripting as usual.
TL;DR: Everything will remain functional, but I'll add a modding/config layer on top of it.

What do you think?
It's alive!!
I've just posted a functional prototype to GitHub: https://github.com/RigsOfRods/rigs-of-rods/pull/3018

  • A new mod type is recognized: *.mission - can add terrain objects, procedural roads and races to existing terrain.
  • A new archive type is recognized: *.missionzip
  • Mission types: only "race", more to come.
  • To load a mission, open in-game console and say 'loadmission <filename>'
  • Unloading missions is not implemented yet, use "top menubar / simulation / reload current terrain" to remove mission.
What the '.mission' file looks like (provisional documentation):
The full example file is attached on GitHub with instructions.

; -----------------------------------------------------------------------------
; Rigs of Rods (www.rigsofrods.org)
; Example mission (race)
; https://forum.rigsofrods.org/threads/mission-system-prototype.3846/
; -----------------------------------------------------------------------------

; Generic info for the game
; -------------------------
; These values will be processed by modcache and appear in the Selector UI

mission_format_version 1
mission_name "Simple Test Circuit"
mission_type "race"
mission_guid "450b1452-418b-4c7b-ac72-5813158f4907"
; A GUID of the terrain - here simple2.terrn2
mission_terrain_guid "dc178e9c-840d-443f-b249-434433ae5fd0"
author "", "Petr Ohlidal"
; Optional user-made script - must implement functions `bool loadMission(string mission_filename, string resource_group);` and `void unloadMission();`
; If not provided, a bundled script 'mission_default.as' will be used which does all the necessary setup and .mission file processing.
;mission_script mission_default.as

; Mission data sheet
; ------------------
; These items will be added to the terrain on mission load and removed on mission unload.

; same syntax as in .tobj file, except processed by script.
; Tip: use `road_editor.as` to create/edit roads, then copy the data from file 'logs/editor_out.log'
    smoothing_num_splits 2
       531.462830,      0.300006,    486.630127, 0,    179.891647, 0,      6,      0.000000,      0.000000, both
.. too long ..
       531.462830,      0.300029,    486.630127, 0,    178.722961, 0,      6,      0.000000,      0.000000, both

; Generic checkpoints, processed depending on 'mission_type'
; Tip: use survey map to create AI waypoints + 'Export' button in Top menubar -> Vehicle AI panel.
;      Copy the exported waypoints from RoR.log and remove the JSON formatting.
; Advanced tip: use `road_editor.as` to generate AI waypoints from procedural roads, then follow above Tip.

    ; This directive adds an extra rotation to all lines following it. Default value is no rotation (0, 0, 0).
    ; Syntax: 'add_rotation degreesX, degreesY, degreesZ'
    ; By convention, the checkpoint objects are defined sideways (facing X axis),
    ; but here we use rotations from the procedural roads which face Z axis.
    ; PORTING NOTE: The checkpoints in current map scripts don't need this, they're pre-rotated.
    set_extra_rotation 0, 90, 0
        531.462830, 0.300006, 486.630127, 0,    179.891647, 0
.. too long ..
        531.462830, 0.300029, 486.630127, 0,    178.722961, 0
Screenshot from Simple Test Terrain:

Last edited:
Great work!

While playing on multiplayer for a few hours yesterday, I dreamed up a multiplayer events concept inspired by GTA Online's freemode challenges and FH's Horizon Arcade / Forzathon LIVE. The missions system would help make this a reality:
  • An event would trigger at random once every 30 minutes, or at any time with a moderator-only chat command. Time per event should be adjustable via server config.
  • Details about the upcoming and current event appear under the player list.
  • Event type ideas:
    - Top speed, highest jump, longest drift, etc
    - Races, either circuits or point to point. Teleporting, server/AngelScript commands (!boost), and resetting would be disabled. Collisions are disabled for those not participating in the race. Could also restrict all players to a specific vehicle.
    - Checkpoints, up to 100 placed around the terrain and the player who collects the most in the time limit wins.
  • Each event will reward players points depending on a player's performance. Those with a valid user token will have their points tracked on a per-server leaderboard.
@CuriousMike Thanks for the suggestions, they sound really fun and I think we already pretty much have all the features to script this:
  • This will involve server scripting - I already created an example script https://github.com/RigsOfRods/ror-server/pull/150 and right now also added scripting doc: https://github.com/RigsOfRods/rigs-of-rods/pull/3018#issuecomment-1428416850
  • The critical tools will be `game.serverCmd(string)` on client side and `server.cmd(int, string)` on server side. The former sends an arbitrary string to server which processes it using `gameCmd()` callback - a test script for this is attached here https://github.com/RigsOfRods/ror-server/pull/150. The latter executes an AngelScript snippet on the client, IIRC this was already used before to create interactive servers.
  • Right now, the "terrain_default.as" script doesn't load the race system or mission system, but it's a simple change. You will be able to create races or missions just by sending "races.addRace(...);" snippet or the like.
  • We can already draw DearIMGUI from a script. You cannot do it directly from the server but you can have pre-made GUI scripted on the terrain and just control it using `server.cmd(...)`. I can add such premade GUI to the default terrain script if you want.
To address your points (arranged in bullets as you wrote it):
  • Event triggering: with `server.cmd()` you can do anything anytime. Moderator-only chat command: no problem, there's the `playerChat()` callback for that. Adjustable via server config: The main server config file is rigid and requires rorserver codechange; you should use LocalStorage instead and configure it with .asdata file.
  • Details will appear ... (GUI): Like I said above, you can't draw GUI directly from the server but you can have some pre-made locally and control it from server, like this:
    // ------- client side --------class ServerEventInfo {
       string text;
       void Draw() { ImGui::TextDisabled("Server event info:"); ImGui::Text("text); }
    ServerEventInfo g_serverEventInfoGUI();
    void frameStep(float dt) { g_serverEventInfoGUI.Draw(); }
    // --------- server side ----------------
    server.cmd(TO_ALL, "g_serverEventInfoGUI.text=\"Get ready everyone!\"; ");
    The only missing piece is getting the screen position of the player list UI so you can draw directly under it. I'll add that because I want to maximize the value of the scripted GUI as opposed to editing the in-game UI directly.
  • Event type ideas:
    - Top Speed: Just use `game.getNumTrucks()` and `game.getTrucksByNum()` to enumerate actors and finally `actor.getSpeed()` and `actor.getVehiclePosition()` to evaluate state and report to server using `game.serverCmd()`. Highest jump: we don't have a "contact with ground lost/gained" events ATM, I can add them like I added the any-node-eventbox feature. Longest drift: Tricky one, we have some wheel slip tracking (for skidmarks) but currently not reachable via angelscript. To be looked into.
    - Races: Existing race system has the ability to hide and reveal races, so you can toggle races on the terrain from the server using `server.cmd()` and with a small change in default terrain script also create races from server. Disabling feats: I'll have to code this, I imagine something like `game.setEventEnabled(EV_COMMON_REPAIR_TRUCK , false)` for keyboard-triggered things and `game.setGuiFeatureEnabled(GUI_FEATURE_SURVEYMAP_TELEPORT, false)` and finally `game.setConsoleCmdEnabled("goto", false)` Restricting to specific vehicle: Just use an eventbox around the whole race area and use `actor.getTruckName()` and `getTruckFileName()` to kill non-conforming trucks.
    - Checkpoints+timelimit: entirely doable by existing race system. See what it can do: https://developer.rigsofrods.org/d9/dce/class_script2_script_1_1races_manager.html
  • Points: just scripting + `game.serverCmd()`. Leaderboard: just local GUI scripting + `server.cmd()` as described above for the EventInfoGUI
Last edited:
For the record:
I created a new mission type for the prototype: "stunt". It uses 2 event boxes and records the travel between them. When completed, it pops up a dialog with results. This is of course temporary until we devise better scoreboard solution.

The screenshot is from my test mission 'simple2ramp.mission' attached at GitHub: https://github.com/RigsOfRods/rigs-of-rods/pull/3018#issuecomment-1447111854 - as you can see, Dodge Viper didn't take the jump too well.
I'm back at this, see my today's blog: https://forum.rigsofrods.org/thread...-pick-up-the-scripting-slack.3995/#post-20599. For those lazy to click, I'm going to create a multiplayer game of tag, to have demo for the mission system and to develop a working multiplayer support.

My brain is somewhat reluctant to put together a complete technical design, so I'll scribble it here from passing thoughts:

The gameplay:
  • Host picks who gets the tag first, or leaves it random.
  • To pass a tag, player must drive close to the opponent, face them forward and press a key. This scores a point.
  • Victory conditions (set by host on the start): score, time limit or both.
  • Survival mode: The tagged player must pass the tag within timeout, or they lose a point. If they had no points they lose.
What tools we have:
  • on client it's `game.sendGameCmd(text)` which sends plain text to server script. Server script gets it as-is from "gameCmd" callback.
  • On server, we have `server.cmd(uid, angelscript)` which sends an angelscript snippet to a client script by UID. There's a dummy UID "TO_ALL" which sends it to all players. The client executes the snippet as part of the terrain script.
    [CODING NEEDED] There's currently no way to have it also invoked in other scripts - I must add this. It's simply because originally the terrain script was the only script. My plan is to make it a pseudo-event to use with existing `game.registerForEvent()` mechanic, like EV_EXECUTE_SERVER_CMDS. And while at it, I'll also add an alternative EV_SERVER_CMD_RECEIVED which treats the commands as ordinary text and delivers it via callback.
What state we need to keep on clients:
  • UID of the player who has the tag, apparently. Must be updated each time it changes.
  • Scoreboard data: UIDs of all players and their score. To be updated each time it changes.
  • Game settings: max score, time limit, survival mode.
  • Mission system status: UID of the host, mission name and "in progress?" indicator.
How it needs to communicate (aka Mission Protocol draft):
  • Host (client) picks the missionzip from modcache. Game sends cmd MISSION_LOADED <name> <host UID> to server which broadcasts it to players.
  • Clients check they have the missionzip. If not, they reply the server with MISSION_DECLINE <name> <host UID>, otherwise MISSION_JOIN <name> <host UID>.
  • Players who joined appear on the scoreboard - server sends MISSION_SCORE <name> <host UID> <player UID> <player score> to all clients. While mission is not in progress, the score means: -1= declined, 0=not ready, 1=ready.
  • Host configures the game by sending MISSION_STATUS <name> <host UID> <fieldName> <fieldValue> to the server, server distributes it to players. The mission system doesn't differentiate between settings and gameplay state, and this generic message updates one state field at a time.
  • Players either send MISSION_LEAVE <name> <host UID> or MISSION_READY <name> <host UID> <readystate>. Server updates the scoreboard.
  • Host sends MISSION_START <name> <host UID> to server which broadcasts it to all clients. Game is on!
  • To perform game action like tagging a player, game sends MISSION_ACTION <name> <hostUID> <actionType="tag!"> and server forwards it to host.
  • Host evaluates the action and updates game state (MISSION_STATUS) and scoreboard (MISSION_SCORE) accordingly.
  • Host ends the mission by MISSION_STOP <name> <hostUID> - players remain joined and can go ready again.
  • Host disbands the lobby with MISSION_UNLOADED <name> <hostUID> - players still see the scoreboard until they close it.
Last edited:
The game is in "free roam" mode by default, you can spawn/teleport/mouse-drag things and so on. The mission author must have the option to block unwanted features. Below is a preliminary list. Suggestions are welcome.