Anaminus / rbxmk

A tool for processing Roblox files.
MIT License
109 stars 11 forks source link

Instance behaviors #71

Open Anaminus opened 2 years ago

Anaminus commented 2 years ago

Each non-property field on an instance can be thought of as a "behavior". These behaviors can be configured. When an instance is indexed, the the combination of the instance's class and the given index can be used to look up a behavior.

For example, when an instance of class "DataModel" is indexed with the string "GetService", this is checked against a behavior descriptor. If the descriptor has a behavior for that combination, that behavior is used. A descriptor might define instances of class DataModel with field GetService to have a behavior "ServerProvider.GetService". This behavior causes the GetService method to be returned, which then behaves as the GetService method does currently.

This feature extends descriptors with the $rbxmkBehavior field. This is separate from descriptor extensions (#65) because the content of of the field has its own distinct structure:

// Fields with "Foo" are stand-ins for some defined value.
{
    "$rbxmkBehavior": {
        "Classes": { // Set of classes that define behaviors.
            "FooInstance": { // Name of a class to which behaviors will be applied.
                // If false, prevent behaviors being applied to subclasses of
                // FooInstance. Defaults to true.
                "Inheritable": false,
                // Map of fields to behaviors. Each key is the name of a field that
                // indexes an instance of FooInstance. The field need not exist, or
                // have a corresponding descriptor. The "sym." prefix causes a
                // symbol to be used. Each value is the name of a predefined
                // behavior. While it may not always be the case, behaviors are
                // usually derived from some instance property or method, in which
                // case their name will have the form "<class>.<member>".
                // Regardless, this is only a name.
                "Behaviors": {
                    // Properties can be mapped to property behaviors.
                    "FooProperty": "Instance.Property",
                    // Methods can be mapped to method behaviors.
                    "FooMethod": "Instance.Method",
                    // A field name can be a symbol.
                    "sym.FooSymbol": "Instance.Symbol",
                },
            }
        }
    }
}

Most of the features planned to use descriptor extensions are more suited to use behaviors instead. The following list gives an overview of possible behaviors that could be implemented from current and planned features:

Notably, Instance.ClassName cannot be a behavior, because the class is needed to resolve which behavior to select. It could be defined under $rbxmkConfig instead, though.

The default global descriptor would define the expected class/fields to map to their expected behaviors. This would make all behaviors available by default, while giving the user the option to fully customize instance behavior.

Symbols

If symbols can be mapped, then should the implementation of the sym library be revised? Instead of having a defined set of symbols, the sym library would generate a symbol for the indexed name on the fly, allowing any symbol name to be used. Then behaviors can be mapped to any arbitrary symbol instead of from a limited baked-in set.

On the other hand, behaviors could eliminate the need for symbols entirely. The purpose of symbols is to have fields that are forward-compatible with any additions Roblox might make to instances. If all behaviors are user-definable, then they can just be redefined to make way for any additions from Roblox.