Skip to main content

2026-08-06 Creating content using Cilbox in Basis

Cilbox is a scripting system that is available by default in Basis. User-generated content such as worlds, props, and avatars may contain scripts, and they will execute in separate sandboxes that have different lists of permitted types and accesses to functions.

Setup

The class should be marked as [Cilboxable], such as:

[Cilboxable]
public class Pencil : MonoBehaviour {
// ...
  • To use a scene that uses Cilbox scripts, a single Cilbox Scene Basis component needs to exist somewhere in the scene.
    • Scene scripts written using Cilbox can be placed outside of that component.
  • To use a prop that uses Cilbox scripts, a single Cilbox Prop Basis component should be put at the root of the prop.
  • To use an avatar that uses Cilbox scripts, a single Cilbox Avatar Basis component should be put at the root of the avatar.

Those components do not need to be configured.

OnEnable is not called the first time

Cilbox seems to have a quirk, where OnEnable will not be called the first time the prop is enabled.

You may have to resort to workarounds similar to the following:

private bool _isEnabled;
public void Start()
{
// Do one-time initialization stuff here.

WhenEnable();
}
public void OnEnable() { WhenEnable(); }
private void OnDisable() { _isEnabled = false; }
private void WhenEnable()
{
if (_isEnabled) return; // Cilbox quirk
_isEnabled = true;

// Do OnEnable stuff here.
}

Networking

To make an object networkable, either:

  • Call _networkShim = Basis.SafeUtil.MakeNetworkable(this),
  • or add a BasisNetworkShim component and reference it as a field.

Check ownership using _networkShim.IsLocalOwner()

To send and receive data:

  • Send packets using _networkShim.SendCustomNetworkEvent(bytes, ...)
  • receive packets by registering an event listener using _networkShim.NetworkMessageReceived += OnNetworkMessageReceived

Props load at different times for different players, so you may have to detect when network is ready using _networkShim.NetworkReady += OnNetworkReady to start sending packets.

According to the creator of the Basis Framework, it is impossible for other players to load the prop faster than the person who spawns and owns the prop, therefore when OnNetworkReady is triggered on a non-owner, it should be safe to assume that the network owner is ready to receive the packets. EDIT: It is unclear if that's the case.

UI Buttons

Follow the official Canvas UI documentation, then.

To make a UI button trigger events when clicked:

  • Reference the Unity UI button as a field, and then
  • register an event listener, such as _eraseAllButton.onClick.AddListener(OnEraseAllClicked);

Pickups

Follow the official Interactable documentation on how to set up a network synced pickup.

To detect pickup and drop events:

  • Reference the BasisPickupInteractable component as a field, and then
  • register an event listener, such as pickup.OnInteractStartEvent.AddListener(OnPickup); and pickup.OnInteractEndEvent.AddListener(OnDrop);
  • for the trigger events, use pickup.OnPickupUse.AddListener(OnPickupUse). The callback method has an enum value that tells you when the trigger press starts, continues being held, or ends.

Pickups do not affect component ownership.

  • If a user grabs a synced pickup, they only own the synced pickup component.
  • The other networked components that are on the same GameObject do not change ownership.

Teleportation

Scene props can teleport the player using by calling the BasisLocalPlayer.Instance.Teleport(spawn.position, spawn.rotation) function.

Troubleshooting builds

If a non-uploaded prop works in editor but doesn't work when the built BEE file is loaded in the real application, then consider loading the BEE file inside the Unity Editor while in Play Mode and debug it directly inside the editor.

This way, I found out that the non-uploaded prop worked in editor because it was executing using the Cilbox Scene box component instead of the Cilbox Prop box component, as the prop didn't have a Cilbox Prop box component attached.

Not supported

Do not use Array.Empty<HVRNPSCilBeacon>()

Privilege failed for System.Array.Empty generic argument 0 type HVR.NPS.ForCilbox.HVRNPSCilBeacon

CilboxException: Error: Could not find reference to: [mscorlib][System.Array][HVR.NPS.ForCilbox.HVRNPSCilBeacon[] Empty[HVRNPSCilBeacon]()] Type from:System.Array

Do not use lambdas

ArgumentException: Object of type 'Cilbox.CilboxHeapInstance' cannot be converted to type 'Cilbox.CilboxProxy'.

Do not implicit cast from Vector4 to Vector3

  • Error raised on method op_Implicit of type UnityEngine.Vector4 with 0 args.
  • AmbiguousMatchException: Ambiguous match found.

Bugs

Negating a value is not working properly

The value of x in var x = -someValue; will be incorrect.

  • Discovered when Random.Range(-x, x) was returning values way out of expected range.
  • This can be worked around by multiplying with -1

This bug has been fixed in the Cilbox repository, but it may be reflected in the Basis Framework project and client at a later point.

Server XML configuration ignores scale

The scale of props is ignored in the XML configuration.

2026-07-13 What causes the Export .unitypackage function to include so many assets?

When exporting a custom avatar using Unity's default Export .unitypackage function to transfer assets between projects, assets are often scattered across multiple folders as expected from a custom avatar.

However, the contents of the export often end up with extra assets that aren't particularly needed by the custom avatar.

All of these reasons can be summarized using the picture below, but the first reason listed here can be very unexpected. I suggest you read the first reason in detail, and skim over the others since many of them are predictable once you're aware of it.

before.png

A prefab instance simultaneously overrides a field and deletes the component that contains that field

If a prefab instance overrides an asset reference on a component, and that prefab instance simultaneously removes the same component or GameObject which is present in the prefab, the override referencing the asset continues to exist.

In addition, the Unity Editor prefab override UI is incorrect and will not display overriden references inside removed components. The act of un-removing the component will expose the hidden reference that it contained.

Detecting this issue is nearly impossible without external tooling.

A reference inside the active hierarchy points to a Transform located within a prefab source that you do not use

Components may contain stray references to sub-assets of a model or a transform within a prefab, even if you are not using that prefab source on your avatar.

This is a problem because it means every reference used by that prefab source will be required, along with any other asset referenced by them.

On my projects, I've found that the anchor override of a SkinnedMeshRenderer sometimes points to a transform located inside a prefab source.

A reference points to a sub-asset of a prefab model

If a sub-asset of a prefab source or model is required (such as Mesh), then the entire prefab source is required.

This is expected, but it can cause a problem because of the next reason.

A prefab model (FBX) contains references to textures that you do not use

Sometimes, textures located inside the project are referenced directly by the prefab model (FBX).

Even if you do not use the texture in any of your active materials, it might get included.

A prefab source requires assets that you have overriden

A prefab source may be set up with asset references pointing by default to assets that you are not using because they are overriden by your prefab instance or prefab variant.

This is typically true when you are using avatars that you have purchased, but you have overriden some materials.

In turn, these assets may reference other assets, such as textures, that you may also not be using.

A prefab source requires assets in a Component or GameObject that you have removed

Very similar to the previous reason, but instead of overriding the asset with another asset, you have removed the component or GameObject altogether.

A prefab instance or a prefab source requires assets in GameObject which is EditorOnly

As an alternative to removing a Component or GameObject, it can be common practice to set it to EditorOnly so that you can preserve the objects in order to re-include them later.

Not only the prefab source may reference assets that you are not using; it is possible that you are yourself overriding some fields using assets on the prefab instance, which will not end up being used.

It is ambiguous whether this counts as an unused asset as different people will use EditorOnly differently in their workflow, but it is worth a mention.

A component or material requires a script, and references inside those scripts

Some files containing code are not necessarily wanted during export, but also, these scripts can reference other objects.

  • Materials require a shader. Shaders are often installed separately.
    • Shaders sometimes contain references to default textures in them. Those textures are often installed with the shader itself.
  • If a GameObject references a MonoBehaviour, it may require scripts, and DLLs. Those are often installed separately.
    • If a MonoBehaviour uses an icon, then that icon can end up being included.

If you are exporting to another game, some references inside components may be undesirable

If you are exporting from one game to another game, some references inside components can be irrelevant.

  • Some components may be irrelevant in another project (e.g. Modular Avatar Menu Item) and contain references to assets such as icon Textures.
  • Some proprietary assets may be incompatible in another project (e.g. Expression Menu), and those assets may themselves contain references to other assets.
  • Some Unity assets may be irrelevant in another project (e.g. Animator Controller, Animation Clip).

Before and after

How it looks like before (this is the same picture as the one at the top of this article):

before.png

And after:

after.png