Big dho moment for me ... async message handlers run on background threads. I had several API I could never get to work- ShowEntity, Teleport, ShowGameMessage, MoveEntity Any ModAPI call that touches a live Unity object — LocalPlayer, entity positions, GUI methods, playfield mutations- all return null or throw when run from a background thread. Wrapping these calls in the mod's main thread dispatcher resolves it. Probably obvious to Unity developers; worth a note for those of us coming from other backgrounds. If the devs could add something to this effect to the ModAPI docs it could really help anybody who hasn't worked with Unity before.
With the default config the scripting uses the ThreadPool for API aceess Code: private void ExecNext(IScriptRootData data) { ((PlayfieldScriptData)data.GetPlayfieldScriptData()).IncrementCycleCounter(data.ScriptId); _ = EmpyrionScripting.Configuration.Current.ExecMethod switch { ExecMethod.ThreadPool => data.ScriptNeedsMainThread ? DirectExecNext(data) : ThreadPoolExecNext (data.ScriptId), ExecMethod.BackgroundWorker => data.ScriptNeedsMainThread ? DirectExecNext(data) : BackgroundWorkerExecNext(data.ScriptId), ExecMethod.Direct => DirectExecNext(data), _ => false, }; } But some command (e.g. modify items in a container) need to be executed within the main thread in the public void Game_Update() method