#
Mixins
#
Post-0.6.0
#
Preparation
Make sure you are importing MixinType from PolyTypes.js:
import { PolyMod, MixinType } from "https://cdn.polymodloader.com/cb/PolyTrackMods/PolyModLoader/<version>/PolyTypes.js";
The deprecated MixinTypes from pre-0.6 have been entirely removed, left with only these:
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.
#
Mixin Types
Every type of mixin in some way uses the MixinArgs type:
For INSERT:
type MixinArgs: {
type: MixinType.INSERT,
token: string | MixinToken,
func: string | Function
}
For REPLACEBETWEEN:
type MixinArgs: {
type: MixinType.REPLACEBETWEEN,
tokenStart: string | MixinToken,
tokenEnd: string | MixinToken,
func: string | Function
}
For REMOVEBETWEEN, it is the same as REPLACEBETWEEN but without a func.
You might have noticed the MixinToken type. This is another QOL addition to PolyModLoader for 0.6 and above, and is declared like so:
type MixinToken: {
token: string,
occ: number
}
token is simply the string to look for, and occ is the nth occurrence to look for. For example, { token: "));", occ: 4} represent the 4th appearance of the token ));.
#
Mixin Functions
There exists multiple functions that can be called to register mixins:
// Class / funcs in main.bundle.js
registerClassMixin(scope: string, path: string, mixinArg: MixinArgs): void;
registerFuncMixin(path: string, mixinArg: MixinArgs): void;
registerClassWideMixin(path: string, mixinArg: MixinArgs): void;
// Sim worker (currenetly unimplemented)
registerSimWorkerClassMixin(scope: string, path: string, mixinArg: MixinArgs): void;
registerSimWorkerFuncMixin(path: string, mixinArg: MixinArgs): void;
// Global ish
registerGlobalMixin(mixinArg: MixinArgs): void;
registerChunkMixin(bundleName: string, mixinArg: MixinArgs): void;
A few things to note:
- Because of bundling changes in PolyTrack 0.6.0, class/classwide and func mixins don't work on a considerable about of classes (notably Three.js related), which makes global mixins the only option for those classes.
- Another change brought to 0.6.0 was the separation of the editor and garage code into their own separate webpack chunks, thus the need for chunk mixins.
#
Pre-0.6.0
#
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 usingREPLACEBETWEENwith 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 classCLASSREPLACE: Replace Between but classCLASSREMOVE: 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 inmain.bundle.js.registerClassMixin(...): Register a mixin on a function inside of a class inmain.bundle.js.registerClassWideMixin(...): Regsiter a mixin on an entire class in `main.bundle.js (includes constructor!!).registerSimWorkerFuncMixin(...): Register a mixin on a standalone function insimworker.bundle.js.registerSimWorkerClassMixin(...): Register a imxin on a function inside of a class insimworker.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 if the function is not static) 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.