Headless client
Hey! The official Resonite wiki is out. It means this is now unmaintained.
The headless client is basically the game without the user interface, allowing you to host somewhat permanent sessions, like with a server.
In order to access the headless client, you need to be a supporter on the Resonite Patreon with a membership of Discoverer or more.
Downloading the headless client
Note: before proceeding, I can recommend creating a dedicated Steam account for the headless. Doing so will make it easy to automate the process with Docker or scripts later.
Bare metal
To obtain the headless, you currently will need SteamCMD and the headless beta code (can be obtained by sending /headlessCode
to the Resonite bot).
Note: it is also possible to download the headless using the regular GUI Steam client, for that right click on Resonite, select “Properties”, go in “Betas” and enter the headless code into the text box. You should now have access to the headless client.
With SteamCMD, the command is fairly simple (note, it will install Resonite in the SteamCMD directory, you can change the path of the +force_install_dir
to change that):
bash steamcmd.sh \
+force_install_dir ./resonite \
+login SteamAccountName 'SteamAccountPassword' \
+app_license_request 2519830 \
+app_update 2519830 -beta headless -betapassword TheHeadlessCode validate \
+quit
Note: if SteamCMD is giving you any trouble like
steamcmd.sh: Not a directory
, replacebash steamcmd.sh
by./steamcmd.sh
This will install the headless client into the directory specified, you can now go on to the configuration.
To run the headless client, you can go within the directory and do mono Headless/Resonite.exe
and the headless should launch.
Docker
To run a headless within Docker, I can recommend Shadowpanther’s resonite-headless.
It is fairly easy to use.
As specified in the README, the compose file should look like this:
version: "3.3"
services:
resonite:
image: shadowpanther/resonite-headless:latest
container_name: resonite-headless
tty: true
stdin_open: true
environment:
STEAMBETA: headless
STEAMBETAPASSWORD: ask-bot-for-code
STEAMLOGIN: "your_steam_login your_steam_password"
volumes:
- "./Config:/Config:ro"
- "./Logs:/Logs"
- "/etc/localtime:/etc/localtime:ro"
restart: unless-stopped
Sveken on Discord made some modifications to the compose file to accommodate for modded Resonite.
For that, you will need to download Resonite Mod Loader, create a directory moddedhead
in the data directory where you want to place the files of your headless and place inside the NML files.
Then you can use the following compose file:
version: "3.3"
services:
resonite:
image: shadowpanther/resonite-headless:latest
container_name: modded-headless
tty: true
stdin_open: true
command: mono /home/steam/resonite-headless/Headless/Resonite.exe -LoadAssembly "/home/steam/resonite-headless/Headless/Libraries/ResoniteModLoader.dll" -HeadlessConfig /Config/Config.json -Logs /Logs
environment:
STEAMBETA: stuff
STEAMBETAPASSWORD: things
STEAMLOGIN: "username password"
volumes:
- "/data/moddedhead/Config:/Config:ro"
- "/data/moddedhead/Logs:/Logs"
- "/etc/localtime:/etc/localtime:ro"
- "/data/moddedhead:/home/steam/resonite-headless"
restart: unless-stopped
This version will hijack the start script to instead run the headless with the -loadAssembly
command, thus making it possible to run the headless with mods.
Now that you have this setup, you can proceed to the configuration section.
Launching the headless is as easy as docker compose up -d
and docker compose logs
to see logs (add -f
to it to follow them in real-time).
Configuration
The default headless configuration is as follows:
{
"$schema": "https://raw.githubusercontent.com/Yellow-Dog-Man/JSONSchemas/main/schemas/HeadlessConfig.schema.json",
"comment": "DO NOT EDIT THIS FILE! Your changes will be lost. Copy it over and create a new file called Config.json",
"universeId": null,
"tickRate": 60.0,
"maxConcurrentAssetTransfers": 4,
"usernameOverride": null,
"loginCredential": null,
"loginPassword": null,
"startWorlds": [
{
"isEnabled": true,
"sessionName": null,
"customSessionId": null,
"description": null,
"maxUsers": 16,
"accessLevel": "Anyone",
"useCustomJoinVerifier": false,
"hideFromPublicListing": null,
"tags": null,
"mobileFriendly": false,
"loadWorldURL": null,
"loadWorldPresetName": "Grid",
"overrideCorrespondingWorldId": null,
"forcePort": null,
"keepOriginalRoles": false,
"defaultUserRoles": null,
"roleCloudVariable": null,
"allowUserCloudVariable": null,
"denyUserCloudVariable": null,
"requiredUserJoinCloudVariable": null,
"requiredUserJoinCloudVariableDenyMessage": null,
"awayKickMinutes": -1.0,
"parentSessionIds": null,
"autoInviteUsernames": null,
"autoInviteMessage": null,
"saveAsOwner": null,
"autoRecover": true,
"idleRestartInterval": -1.0,
"forcedRestartInterval": -1.0,
"saveOnExit": false,
"autosaveInterval": -1.0,
"autoSleep": true
}
],
"dataFolder": null,
"cacheFolder": null,
"logsFolder": null,
"allowedUrlHosts": null,
"autoSpawnItems": null
}
Configuration keys
Basics
Name | description | type | Required |
---|---|---|---|
comment | A small bit of text describing your configuration. Useful if you have multiple. | string | No |
loginCredential | The username for the headless client account. | string | Yes |
loginPassword | The password for the headless client account. | string | Yes |
startWorlds | All the worlds to run on this headless (see the worlds section) | Special | Yes |
tickRate | How many updates per second should the headless do (default on 60). | integer | No |
maxConcurrentAssetTransfers | The maximum amount of asset transfers for the headless (default on 4) | integer | No |
usernameOverride | Sets a username different from loginCredential that will be displayed in the world list and session users tab. | string | No |
dataFolder | Sets the path to the data store. | string | No |
cacheFolder | Sets the path to the cache folder. | string | No |
logsFolder | Sets the path to the logs folder. | string | No |
allowedUrlHosts | Lists of hosts which will be allowed for WebSocket/HTTP operations. | array[string] | No |
universeId | Sets which universe the headless is in (no documentation on universes as of yet). | string | No |
Worlds
Note: you can add as many worlds as you wish, just add a new one in the array.
Name | description | type | Required | Example |
---|---|---|---|---|
sessionName | The name that will show up in the world browser (defaults to the world name if not present). | string | No | “awa awa” |
customSessionId | Sets a custom session ID starting with S- and respecting the format <userid>:<sessionid> |
string | No | “U-Awa:hangout” |
description | Sets the description of the session (defaults on world description if not present) | string | No | “Awa awa kabawa” |
maxUsers | Sets the maximum amount of users in the session (defaults to 32) | integer | No | 1337 |
accessLevel | Sets the access level of the session (Private, LAN, Friends, FriendsOfFriends, RegisteredUsers, Anyone; defaults to Anyone) | Any | No | RegisteredUsers |
hideFromPublicListing | Hides the session (defaults on false) | boolean | No | true |
tags | List of tags to assist with discoverability. | array[string] | No | [“awa”, “hangout”] |
mobileFriendly | Is the session friendly for mobile/quest users. | boolean | No | false |
loadWorldURL | Loads a world to host from a resrec:/// URL. |
string | No | “resrec:///G-Resonite-Team/R-c3f6fd32-d82f-4f48-80f1-87a05b184650” |
loadWorldPresetName | If nothing in loadWorldURL, will create a new world following the preset. | string | No | “Grid” |
overrideCorrespondingWorldId | Overrides the world ID, allowing it to be nested with other sessions with the same world ID. | Any | No | |
forcePort | If specified, the session will run on a specific port. | integer | No | 1911 |
keepOriginalRoles | If enabled, the headless will not override the default roles of the hosted world. | boolean | No | true |
defaultUserRoles | Sets roles for different users. | Object | No | {“j4”: “Admin”, “DN0”: “Guest”} |
roleCloudVariable | Name of a cloud variable to determine user roles. | string | No | “G-mygroup.headless.perms” |
allowUserCloudVariable | Name of a cloud variable to determine who can join the session. | string | No | “G-furdation.headless.whitelist” |
denyUserCloudVariable | Name of a cloud variable to determine who cannot join the session. | string | No | “G-furdation.headless.blocklist” |
requiredUserJoinCloudVariable | Name of a cloud variable to determine if a user can join the session. | string | No | “G-furdation.headless.privateAccess” |
requiredUserJoinCloudVariableDenyMessage | Message to display to users not on the whitelist. | string | No | “You need to be verified to join this session” |
awayKickMinutes | How many minutes can people stay AFK in the session without being kicked (-1 disables it) | integer | No | 5 |
saveAsOwner | Controls who saves this world when saved. | Any | No | |
autoInviteUsernames | Invites all the users specified to the headless on start. | array[string] | No | [“j4”] |
autoInviteMessage | Sets a message sent before the invite to the headless is sent. | string | No | “Come and join!” |
parentSessionIds | Provides a list of parent sessions. | array[string] | No | |
autoRecover | ? | boolean | No | |
idleRestartInterval | Will restart the world automatically after X time elapsed if the session is empty. | integer | No | 55 |
forcedRestartInterval | Will restart the world automatically after X time elapsed no matter what. | integer | No | 55 |
saveOnExit | If set to true, this world will be saved when shutting down the headless. | boolean | No | false |
autoSaveInterval | Will save every X time. | integer | No | 50 |
autoSleep | Will prevent empty (or filled with away users) world to run full update cycles. | boolean | No | true |
Commands
There is a bunch of commands that can be used to manage the headless client within its console.
command | description | usage |
---|---|---|
saveConfig | Saves the current configuration into the original configuration file | saveConfig <filename> |
login | Logs into a Resonite account | login <username> <password> |
logout | Logs out of the current account | logout |
message | Sends a message to a friended user | message <user> <message> |
friendRequests | List all friend requests | friendRequests |
acceptFriendRequest | Accepts a friend request | acceptFriendRequest <user> |
worlds | List running worlds | worlds |
focus | Focuses into the specified world (by index) | focus <index> |
startWorldURL | Starts a world from a resrec:/// URL |
startWorldURL <resrec:/// > |
startWorldTemplate | Starts a world from a template | startWorldTemplate <template > |
status | Shows the status of the current world | status |
sessionURL | Displays the session URL | sessionURL |
sessionID | Displays the session ID | sessionID |
copySessionURL | Copies the session URL to the clipboard | copySessionURL |
copySessionID | Copies the session ID to the clipboard | copySessionID |
users | Lists the users in the world | users |
close | Closes the current focused world | close |
save | Saves the current focused world | save |
restart | Restarts the current focused world | restart |
kick | Kicks a user from the sessions | kick <user> |
silence | Silences a user | silence <user> |
unsilence | Removes the silence on a user | unsilence <user> |
listBans | Lists all bans | listbans |
ban | Bans a user | ban <user> |
banByName | Bans a user using an username | banByName <username> |
unbanByName | Unbans a user using an username | unbanByname <username> |
banByID | Bans a user using an userid | banByID <userid> |
unbanByID | Unbans a user using an userid | unbanByID <userid> |
respawn | Forces an user to respawn | respawn <user> |
role | Sets a specific role for a user | role <user> <role> |
name | Sets the session name for the current focused world | name <name> |
accessLevel | Sets the session access level | accessLevel <level> |
description | Sets the session description for the current focused world | description <description> |
hideFromListing | Set the focused world hidden or not | hideFromListing <true/false> |
maxUsers | Sets the maximum amount of users | maxUsers <number> |
awayKickInterval | Sets the amount of time after which inactive users will be kicked | awayKickInterval <time> |
import | Imports an asset in the current focused world | import <resrec:/// or file path> |
importMinecraft | Imports a Minecraft worl into the current focused world | importMinecraft <path to level.dat folder> |
dynamicImpulse | Sends a dynamic impulse in the scene root | dynamicImpulse <tag> |
dynamicImpulseString | Sends a dynamic impulse with string data | dynamicImpulseString <tag> <string> |
dynamicImpulseInt | Sends a dynamic impulse with int data | dynamicImpulseInt <tag> <int> |
dynamicImpulseFloat | Sends a dynamic impulse with float data | dynamicImpulseFloat <tag> <float> |
spawn | Spawns an item saved to the inventory in the root of the world | spawn <resrec:///> <active true/false> |
gc | Forces full garbage collection | gc |
shutDown | Shuts down the headless client | shutDown |
tickRate | Modifies the headless tickrate | tickRate <number> |
log | Switches to logging mode, press enter to return to the interactive console | log |