← Paracosm·Docs/Handbook
Contents · Operations

Operations: building, running and restarting the region

Building OpenSimulator

1. It is .NET 8, not Mono

OpenSimulator 0.9.3.0 builds and runs on .NET 8 (SDK plus the .NET 8 and ASP.NET Core 8 runtimes). Mono is not needed. Build with runprebuild.bat, then compile.bat (which is dotnet build -c Release OpenSim.sln).

2. Run runprebuild.bat from cmd, not from a POSIX shell

Its /excludedir = "obj | bin" argument is mangled when passed through bash.

3. Put the official native SQLite in bin/

The shipped bin/lib64/sqlite3.dll is a Mono dllmap build that links the absent MSVCR90.dll. .NET 8 ignores the dllmap (bin/Mono.Data.Sqlite.dll.config), and DllImport("sqlite3") finds bin/sqlite3.dll first, so the fix is to ADD the official self-contained x64 sqlite3.dll (SQLite 3.47.0 here) as bin/sqlite3.dll; bin/lib64/ stays as shipped. (An earlier note said it replaces the lib64 copy; on the reference server lib64 was never changed.)

4. Building an OpenSim project writes into the live bin/

While the region runs, the copy of a loaded DLL into bin/ fails on the file lock (the output waits in obj/Release/), and up-to-date dependencies are not recopied. Swap the DLL during a restart. After building core OpenSim projects, dotnet build-server shutdown releases the build nodes.

Running headless

5. Use the REST console, not the basic console, with no terminal

The default local console throws with no real console, and -console=basic with a dead stdin busy-loops on EOF and floods the log. Launch from bin/ with dotnet OpenSim.dll -inifile OpenSim.ini -console=rest, with ConsoleUser, ConsolePass and console_port set under [Network] (not [Startup]).

6. Enable OSSL by creating config-include/osslEnable.ini

OpenSim.ini already includes osslDefaultEnable.ini (AllowOSFunctions true), which chains to config-include/osslEnable.ini. Create that file to raise the threat level or allow-list functions, then restart. In this build osSetTerrainTexture (a High-threat function) was allowed to ESTATE_OWNER; scripts added by WorldBridge are owned by the estate owner, so they qualify.

7. Never run two OpenSim processes against one SQLite store

If you run OpenSim under a process supervisor that relaunches it, never also start it by hand.

Logs

All of them are in bin/ (OpenSim's working directory):

file written by size limit
OpenSim.log OpenSim, through log4net rolls at 20 MB; five old files kept, OpenSim.log.1 (newest) to OpenSim.log.5; about 120 MB at most
OpenSimStats.log OpenSim, only while stats record start is on rolls the same way
WorldBridge.audit.log WorldBridge: one line per request (time, caller, role, tok: id, command, body with passwords masked) moved to WorldBridge.audit.log.1 when it passes 16 MB (one old file kept); about 32 MB at most
opensim-console.out.log tools/run_server.py start: OpenSim's stdout and stderr moved to .1 at a start when it is over 20 MB; it still grows while that one process runs

Region-module mechanics

8. A standalone module assembly needs both Mono.Addins attributes

[assembly: Addin("WorldBridge", "0.1")] AND [assembly: AddinDependency("OpenSim.Region.Framework", "0.9.3.0")], with the version string matching OpenSim's VersionInfo exactly, or the plugin scanner ignores the DLL. The class carries [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "...")].

9. Re-arm persisted behaviour on OnPrimsLoaded

RegionLoaded fires before the stored objects are in the scene. Look-ups there miss.

10. Module API notes (so they need not be re-derived)

Colour: part.SetFaceColorAlpha(-1, rgb, alpha) (face -1 = all). Terrain: scene.Heightmap[x, y] = h, then ITerrainModule.TaintTerrain(). Shapes: PrimitiveBaseShape.CreateBox/CreateCylinder/CreateSphere. Delete: scene.DeleteSceneObject(sog, false). Look-up: scene.GetSceneObjectGroup(uuid). Animesh flag: shape.AnimeshEnabled; object animation: SceneObjectPart.AddAnimation(animId, name). At the C# level presence.Animator.AddAnimation(uuid, ...) takes an asset id, unlike LSL.

Restarts and deploys

11. Restart only with nobody on the region

Wait for avatars (or state) to report zero. An idle, away visitor still counts. Save first (save_world or console backup).

12. A full save can outlast a 30 s client timeout

A save after touching every object rewrites the whole region. Pass a longer timeout, or poll state until it answers, then read the database.

13. Swap a module DLL without racing a supervisor

Windows lets you RENAME a loaded, memory-mapped assembly. Rename the live WorldBridge.dll aside, copy the new one in, THEN run console shutdown; whatever relaunches OpenSim loads the new DLL.

13a. WorldBridge answers before the region has finished starting

The last startup step creates every object's script instances. A spawn or delete racing that pass crashed the region (a NullReferenceException in SceneObjectGroup.CreateScriptInstances, fatal), twice on 2026-09-25. tools/region_restart.py now returns only after the log says the script instances are initialised, and the core is patched to skip a deleted group (opensim-patches/startup-script-guard).

14. A region restart does not re-run state_entry

Scripts resume their saved state. A controller that applies a setting only in state_entry or on change will not re-apply it after a restart; re-lay the script or have it re-apply on its next tick.

15. A prim restores its last looping sound at startup, before its script runs

Clear it (llStopSound()) before applying a sound mode, or a silenced region plays again after a restart.

16. A restart clears BulletSim ghost bodies

Objects load from the database with phantom already set, so no stray body is built. See physics-and-walkability.md.

The patches in opensim-patches/

Each patch is a unified diff against pristine 0.9.3.0 (opensim-patches/<name>.diff), applied in the order of opensim-patches/series by build/build_server.py; its folder holds the notes.

patch fixes DLL
asset-instr diagnostics: with PARACOSM_ASSET_INSTR=1, logs every GetAssets request, miss and serve (off by default) OpenSim.Capabilities.Handlers.dll, OpenSim.Region.ClientStack.LindenCaps.dll
sqlite-groups Groups V2 stores only to MySQL/PostgreSQL; adds SQLiteGroupsData and the config OpenSim.Data.SQLite.dll
bulletsim-ghost a mesh set phantom (or deleted) while its physics asset is still being fetched gets a new, untracked, solid body OpenSim.Region.PhysicsModule.BulletS.dll
startup-script-guard a group deleted during the startup script pass crashed the region OpenSim.Region.Framework.dll
terrain-script-pbr osSetTerrainTexture from a script set only the legacy terrain textures; a PBR viewer is sent the PBR slots OpenSim.Region.CoreModules.dll
sqlite-offlineim "Offline Message Module V2" stores only to MySQL/PostgreSQL; adds SQLiteOfflineIMData (offline IM on SQLite) OpenSim.Data.SQLite.dll
sqlite-profiles four bugs in the SQLite profile storage: the usersettings table never created, preferences never stored, classified delete, app data OpenSim.Data.SQLite.dll

Each README has the deploy steps for a running server. The two SQLite storage patches must be in OpenSim.Data.SQLite.dll before the config that uses them (config.md): with offline IM set to Offline Message Module V2 on SQLite and an unpatched DLL, OpenSim does not start. docs/upstream/ has a write-up of each patch for the OpenSimulator bug tracker.

Memory and CPU

17. Never resize a SOLID prim from a script

Every PRIM_SIZE / llSetScale change on a non-phantom prim makes BulletSim build a new collision shape that is never freed (about 75 KB each). Moving (llSetPos) does not leak.

18. Find a leak by measuring one suspect at a time

Measure private bytes over 60 s with nothing happening; force gc (no drop means a real leak); yeng suspend / yeng resume isolates scripts (YEngine's commands; scripts suspend belongs to XEngine and does nothing here); yeng ls lists every script with its event count; silence one family and re-measure.

19. Looping keyframed motion costs CPU every tick

OpenSim's keyframe timer moves each looping object about 20 times a second: 266 swaying objects were 16% of a core with nobody on the region. Gate motion on a visitor being near (motion.md).

Logging in with a viewer

20. Select the grid through the grid list, never with --loginuri

Firestorm's command line does not parse a single --loginuri <uri> in this build: the value is silently dropped and the viewer falls back to another grid (once, before the Paracosm patches, to Second Life's login server with the local account's name and password; viewer.md section 4). By hand: add the grid once via Preferences, OpenSim, Grid Manager, "Add new grid" with the login URI (the viewer fetches get_grid_info), then select it on the login screen. From a script: write the grid to the viewer's user_settings/grids.user.xml and pass --set CurrentGrid <host:port> with --login <first> <last> <password>, as tools/run_viewer.py does. Accounts can be made with WorldBridge create_account (quickstart.md section 9).

21. Firestorm's UI is custom-drawn

UI Automation sees only the window frame. Drive anything inside it by screenshot and OCR plus clicks. See verification.md for the look-and-close procedure.

The bot

bot/ is a .NET 8 console project using LibreMetaverse 3.1.6 from NuGet. API differences from the old libomv that cost time:

COLLADA from Blender

Blender 4.2 and later replaced OpenCOLLADA with a stub collada_support extension whose bpy.ops.export_scene.collada exports empty scenes (0 triangles) with no armature or skin options. For a rigged .dae use the SL "Better Collada" add-on (bpy.ops.export_scene.dae) or the real OpenCOLLADA exporter in Blender 4.1 or earlier (bpy.ops.wm.collada_export). WorldBridge's spawn_mesh does not need COLLADA at all.

A heavy build on the same machine starves the region

A parallel C++ build (msbuild /m) alongside the region stalled OpenSim's watchdog by 5-29 s and held its shutdown for minutes. Run builds at idle priority (start /low), or pause them around a restart.

Never write a password to a log

WorldBridge once logged console commands and request bodies verbatim, including reset user password and create_account passwords. Both logs mask password values now. If you script edits to C# regular expressions from Python, check the bytes afterwards: a "\b" typed into a non-raw Python string becomes a backspace character and silently breaks the pattern.