@rbxts/yield-for-character

Yield until all members of a character rig exist, then dot access!

Usage no npm install needed!

<script type="module">
  import rbxtsYieldForCharacter from 'https://cdn.skypack.dev/@rbxts/yield-for-character';
</script>

README

Yield for Character Parts

Usage:

Pass a Player's Character Model into yieldForR15CharacterDescendants or yieldForR6CharacterDescendants, which will yield via yieldForTree from validate-tree until all the members defined in the code exist (all members except those generated by Sound/Animate scripts). The function will return a promise, and, once these members exist, the model with all its members defined will be accessible through the then method.

yieldForR15CharacterDescendants(character).then(char => {
    // All members are properly typed and defined!
    char.Head.face.Texture = ""
});

// alternatively, one may use `await`
async function f() {
    const char = await yieldForR15CharacterDescendants(character);
    char.Head.face.Texture = ""
}

Here is what I envision as a practical use for this library:

import {
    yieldForR6CharacterDescendants,
    yieldForR15CharacterDescendants,
    CharacterRigR15,
    CharacterRigR6,
} from "@rbxts/yield-for-character";
import { Players } from "@rbxts/services";

function yieldForChildOfClass<T extends keyof StrictInstances>(
    model: Instance,
    className: T,
): Promise<StrictInstances[T]> {
    for (const child of model.GetChildren()) {
        if (child.ClassName === className) {
            return child as never;
        }
    }

    return new Promise<StrictInstances[T]>((resolve, reject, onCancel) => {
        const connection = model.ChildAdded.Connect((child) => {
            if (child.ClassName === className) {
                connection.Disconnect();
                resolve(child as never);
            }
        });
    });
}

function doStuffWithR15(rig: CharacterRigR15) {
    rig.RightFoot.Destroy();
}

function doStuffWithR6(rig: CharacterRigR6) {
    rig["Left Arm"].Destroy();
}

async function handleCharacterModel(model: Model) {
    const rigType = (await yieldForChildOfClass(model, "Humanoid")).RigType.Name;

    if (rigType === "R15") {
        const rig15 = await yieldForR15CharacterDescendants(model);
        rig15.Head.Neck.Destroy(); // R15 specific logic :)
        doStuffWithR15(rig15);
    } else if (rigType === "R6") {
        const rig6 = await yieldForR6CharacterDescendants(model);
        rig6.Torso.Destroy(); // R6 specific logic :)
        doStuffWithR6(rig6);
    } else {
        throw `${model.Name} has an unknown rig type! ${rigType}`;
    }
}

Players.PlayerAdded.Connect(({ Name, Character, CharacterAdded }) => {
    print(`Welcome, ${Name}`);
    if (Character) handleCharacterModel(Character);
    CharacterAdded.Connect(handleCharacterModel);
});