Mixins - polytrackmods/PolyModLoader GitHub Wiki

The types of mixins

There are multiple types of mixins, each with their own use cases and limitations:

  • HEAD: Add code to run at the top of a function (not really recommended / deprecated)
  • TAIL: Add code to run at the end of a function (not really recommended / deprecated)
  • OVERRIDE: Override an entire function (not really recommended / deprecated)
  • INSERT: Inserts a piece of code (string) after a given token.
  • REPLACEBETWEEN: Replaces code with some other code between two given tokens.
  • REMOVEBETWEEN: Removes code between twp given tokens. (effectively the same thing as using REPLACEBETWEEN with nothing as the code)

HEAD and TAIL are kind of old, and are really not recommended to use since using INSERT can acheive both in a much better way.

There are also some class wide specific mixins used only in regsiterClassWideMixin(...):

  • CLASSINSERT: Insert but class
  • CLASSREPLACE: Replace Between but class
  • CLASSREMOVE: Remove Between but class

All of these are accessed through the MixinType enum, like MixinType.INSERT.

The different functions for mixins

In case you skipped Init Functions, pml.function() is just calling function() of a PolyModLoader instance.

There are quite a few functions for registering mixins:

  • registerFuncMixin(...): Register a mixin on a standalone (out of a class) function in main.bundle.js.
  • registerClassMixin(...): Register a mixin on a function inside of a class in main.bundle.js.
  • registerClassWideMixin(...): Regsiter a mixin on an entire class in `main.bundle.js (includes constructor!!).
  • registerSimWorkerFuncMixin(...): Register a mixin on a standalone function in simworker.bundle.js.
  • registerSimWorkerClassMixin(...): Register a imxin on a function inside of a class in simworker.bundle.js.

IMPORTANT: Class wide mixins and standalone function mixins DO NOT WORK on functions/classes declared with const.

It is almost 100% necessary to add .prototype to the class name (the scope parameter) when using registerClassMixin and it's simulation worker equivalent.

HEAD / TAIL / OVERRIDE

These are primitive-ish mixin types and I really recommend using the other ones since these have limitations.
The biggest one being that there can only be one mixin per function because of the way they work.

They are so deprecated that they don't work in simulation worker functions, and the functions cannot be replaced with strings.

Usage:

Individual function:

pml.registerFuncMixin(path: String, MixinType.HEAD/TAIL/OVERRIDE, accessors: Array<String>, func: Function)

Function in a class:

pml.registerClassMixin(scope: String, path: String, MixinType.HEAD/TAIL/OVERRIDE, accessors: Array<String>, func: Function)

The parameters are all fairly self explanatory (scope is the class name and should be followed with .prototype) except for accessors. This parameter is the second reason why I really don't recommend using these.

Any variables you want to access that require the scope of the function you're overriding need to be entered in there. For example, if you want to access a 'private' variable named rD, your accessors would look like this:

[`ww(this, rD, "f")`] // ww is the private field getter, "f" means "field"

To access the return value, each element of the accessors array will be appended to you function's arguments.

(e, t, i, rDprivate) => { // (e, t, i) are the arguments from the mixined function, rDprivate is the return value for ww(this, rD, "f")

}

If something isn't working, don't bother contacting me on Discord since I'll just tell you to use the more supported mixin type.

INSERT / REMOVEBETWEEN / REPLACEBETWEEN

These are way more useful and these are what you should be using in most scenarios.

Usage:

Indivudual function:

pml.registerFuncMixin(path: String, MixinType..., firstToken: String, secondTokenOrFunction: String | Function, functionOptional: Function);

Function in a class

pml.registerClassMixin(scope: String, path: String, MixinType..., firstToken: String, secondTokenOrFunction: String | Function, functionOptional: Function);

The parameters are a little less obvious this time around, (scope is the class name and should be followed with .prototype) but still much easier because no more filthy accesssors.
firstToken is always used. It is a string that is part of the function you are trying to mixin into.
secondTokenOrFunction is used as a token for the BETWEENs, but as a function (or stringified code) for INSERT.
functionOptional is a function used exclusively for the REPLACEBETWEENs and can (and should) be left blank when using INSERT or REMOVEBETWEEN.

The functions can either be stringified code or actual functions, but the functions are converted to string. THE CODE DOES NOT RUN IN YOUR MOD'S CLASS'S SCOPE. DO NOT TRY USING this.pml OR OTHER OF YOUR CLASS'S FIELDS..
If you do need that though, you can use ActivePolyModLoader.getMod("<your mod id>").yourModsClassfield instead.

Tokens can contain new lines by pressing enter (NOT with \n). This is more easily done by directly copying multiple lines of code from the main.bundle.js.
Behaviour of mixins with ambiguous tokens haven't been tested so if your mixin isn't working as expected and you have ); as your token, that's most likely the problem.

Class Wide Mixins (CLASS(INSERT/REPLACE/REMOVE))

Class wide mixins use their own MixinTypes, although they are effectively the same thing. These are really useful to be able to change stuff in constructors primarily.

Usage:

pml.registerClassWideMixin(path: String, MixinType.CLASS..., firstToken: String, secondTokenOrFunction: String | Function, functionOptional: Function)

Basically everything from the section above applies except that the class name doesn't include .prototype, so you can read that for the parameters.
You can in theory use this for individual functions, but getting a unique token might be a little more complicated.

⚠️ **GitHub.com Fallback** ⚠️