DOUBLEX'S PROFILE

Just a nameless weakling who's incredibly nub at everything

Search

Filter

[Poll] Is a plugin rewriting RMMZ codebase into ES6 standard a good idea?

I think such a plugin has at least the following potential advantages:
1. Those avoiding direct prototyping like a plague can work with an ES6 version
2. Those not being familiar with ES5 don't have to learn this outdated approach, especially when it's just for writing RMMZ plugins
3. As this plugin can be directly maintained by the RMMZ plugin developer community, it might address their needs more effectively and efficiently(as long as such changes won't break plugins not written with this plugin in mind)

And at least the following potential disadvantages:
1. This plugin will need to be kept in sync with the default RMMZ codebase changes, which can be very complicated and convoluted for the plugin developers involved to detect all the changes in the latter(while letting the plugin users/other plugin developers know if the plugin's outdated is an easy, simple and small task)
2. This can further "encourage and recommend" ES5 vs ES6 flame wars among some plugin developers(and can be a serious community trouble if it becomes a widespread drama), as some will write plugins in the ES6 standard with this plugin and some will continue to write ES5 with direct prototyping without this plugin
3. Some plugin users touching plugin implementations will have to deal with both ES5 and ES6 styles, as some plugins will be written in the former and some will be written in the latter(or even mixed styles in the same plugin in some rare extreme cases)

To make this post more effective and efficient, I've used nearly 35 hours in 3 days to write 1 such plugin(0.9.5 with just the core parts).
While it might break some plugins not written with this plugin in mind despite the fact that I've checked about that several times already, I want the discussion to focus on whether THIS DIRECTION AS A WHOLE is a good idea, so I hope it's not so poorly written that it'll defeat its purpose in this post :)

[RMMZ]Using ES6 Class Inheritance With Extensions For Aliasing Without Direct Prototyping

With ES6 classes, trying to extend the parent class in plugins can lead to its children still using the old parent codes.
For instance, you can place this in a JavaScript sandbox and verify the console output yourselves:
// Default RMMZ codebase
class DefaultMZParentClass {

    method() { return "Parent method"; }

}

class DefaultMZChildClass extends DefaultMZParentClass {

    method() { return `${super.method()} Child method`; }

}
//

// Plugin codebase
const PluginAParentClass = DefaultMZParentClass;
DefaultMZParentClass = class extends DefaultMZParentClass {

    method() { return `Plugin ${super.method()}`; }

};
//

// It's "Parent method Child method" but should be "Plugin Parent method Child method"
console.info("new DefaultMZChildClass().method()", new DefaultMZChildClass().method());
//

So normally, you'll still have to directly type prototypes:
// Default RMMZ codebase
class DefaultMZParentClass {

    method() { return "Parent method"; }

}

class DefaultMZChildClass extends DefaultMZParentClass {

    method() { return `${super.method()} Child method`; }

}
//

// Plugin codebase
const parentProto = DefaultMZParentClass.prototype, pluginAMethod = parentProto.method;
parentProto.method = function() { return `Plugin ${pluginAMethod.call(this)}`; };
//

// It's "Plugin Parent method Child method" which is the intended output
console.info("new DefaultMZChildClass().method()", new DefaultMZChildClass().method());
//


But I wanted to offer an alternative for those not being familiar with ES5 or avoiding prototypes like a plague despite the fact that many RMMZ codes are written that way, and this is what I've come up with:
/*
 * Do these 2 additional things when using ES6 class inheritance aliasing
 * without directly typing prototypes:
 * 1. Add the following code right below a new class inheriting another one:
 *    - ExtendedClassAlias.inherit(Klass);
 *    Where Klass is the new class inheriting another one
 * 2. Add the following code right below extending an existing class as a way
 *    to alias its methods:
 *    - ExtendedClassAlias.updateClass(Klass);
 *    Where Klass is the existing class being extended as a way to alias its
 *    methods
 * Right now it doesn't work well with inheriting static functions in classes,
 * so those in children classes should use ParentClass.staticFunc.call(this)
 * instead of super.staticFunc()
 */

// Codes allowing ES6 class inheritance aliasing without direct prototyping
class ExtendedClassAlias {

    static inherit(Child) {
        const childProto = Child.prototype;
        const parentName = Object.getPrototypeOf(childProto).constructor.name;
        this._inherit(Child, parentName);
    }

    static updateClass(Parent) {
        const parentName = Parent.prototype.constructor.name;
        // There's no need to update anything if the passed class's no children
        if (!this._inheritances.has(parentName)) return;
        this._updateClass(this._inheritances.get(parentName), Parent);
        //
    }

    static _inherit(Child, parentName) {
        // So the parent class will know which classes are its children
        if (this._inheritances.has(parentName)) {
            const oldChildProtos = this._inheritances.get(parentName);
            const newChildProtos = oldChildProtos.concat([Child]);
            this._inheritances.set(parentName, newChildProtos);
        } else {
            this._inheritances.set(parentName, [Child]);
        }
        //
    }

    static _updateClass(children, Parent) {
        this._updateProtoMethods(children, Parent.prototype);
        this._updateStaticFuncs(children, Parent);
    }

    static _updateProtoMethods(children, parentProto) {
        // So all the children will inherit the new rather than the old parent
        children.forEach(Child => Child.prototype.__proto__ = parentProto);
        //
    }

    static _updateStaticFuncs(children, Parent) {
        // So all children will inherit all new static functions from new parent
        Object.getOwnPropertyNames(Parent).forEach(name => {
            const desc = Object.getOwnPropertyDescriptor(Parent, name);
            if (!desc || typeof desc.value !== "function") return;
            children.forEach(Child => {
                Child[name] = Child[name] || Parent[name];
            });
        });
        //
    }

}
ExtendedClassAlias._inheritances = new Map();
//

I've tested that it works for adding new instance variables and prototype methods in base classes, and extending and overriding existing prototype methods there(you can place the linked snippet into a JavaScript sandbox and verify the console output yourselves).
While I failed to inherit the changes in the static functions of the base classes from plugins as well, this can be easily mitigated by using BaseClass.staticFunc.call(this) instead of super.staticFunc().

Basically, the essence of the issue when aliasing ES6 class inheritance without direct prototyping is this:
1. The original child class inherits the original base class
2. A plugin extends the original base class to alias some of its prototype methods
3. The child class still inherits the original base class
So to solve this, simply store the linkage between the child class and the base class right after creating that child class, then points the parent of the child class to the extended base class right after extending it.

As for the static functions in classes, while I tried to use the linkage to let the exiting children class use the new static functions from the extended parent class, I failed to cover the case for aliasing existing parent class static functions, because it's just impossible:
1. The super in the static functions of the child class always points to the original parent class
2. The super in the static functions of the extened parent class always points to the original parent class
3. The super in the static functions of the child class's supposed to always point to the extended parent class
Clearly, combining 1 and 2 will contradict with 3, which is the goal I've trying to achieve.

For those not being familiar with ES5 or avoiding prototypes like a plague, I hope using ExtendedClassAlias won't be too demanding for you, as all you need to do is sticking to these:
/*
 * Do these 2 additional things when using ES6 class inheritance aliasing
 * without directly typing prototypes:
 * 1. Add the following code right below a new class inheriting another one:
 *    - ExtendedClassAlias.inherit(Klass);
 *    Where Klass is the new class inheriting another one
 * 2. Add the following code right below extending an existing class as a way
 *    to alias its methods:
 *    - ExtendedClassAlias.updateClass(Klass);
 *    Where Klass is the existing class being extended as a way to alias its
 *    methods
 * Right now it doesn't work well with inheriting static functions in classes,
 * so those in children classes should use ParentClass.staticFunc.call(this)
 * instead of super.staticFunc()
 */

P.S.: I've spent almost 24 hours on this and I enjoyed the process a lot, even though this might not be practical enough to be used in MZ :)

[SCRIPTING]Will you deliberately write uncommon bad codes with strange poor designs to make plagiarisms easier to prove?

Disclaimer: Plagiarisms should never ever be countered by this idea alone, as this idea, even if it's to be used, should be used along with some other plagiarism counters, otherwise one would fall into the "security through obscurity" application pitfall(but not the idea itself).

While in most cases, only those having high popularity and excellent reputations will have to worry about plagiarisms on a regular basis, sometimes even a dumb noob like me can be a victim on this(and unfortunately a long time ago some of my free plugins are sold by someone else without my permissions, even when it was such a blatant scam that I don't think anyone will really lose a penny there).
So I've read how some of the best and most successful plugin developers deal with plagiarisms, and I noticed that sometimes some uncommon bad codes and strange poor designs in some plugins made by the original plugin developers can actually help proving the plagiarisms, as I've seen a plugin developer listing some of them as evidences.

While I don't think those bad codes/poor designs are intentional(they're likely due to the desire/need to make tons of plugins very quickly), that case of that plugin developer inspired me to think about whether it'd be at least considerable to intentionally write uncommon bad codes with strange poor designs to make plagiarisms easier to prove(of course better implementation alternatives shouldn't be obvious nor easy/simple to swap in or it'd only stop those trying to hide their plagiarisms but are too lazy/stupid to do it well).
The reasoning behind this practice is that, while there won't be many different ways to do the exact same thing and it's likely to fall into the very same common programming pitfalls, if a scammer plagiarizes a plugin using tons of uncommon bad codes and strange poor designs without removing all those rare programming pitfalls, it'd be an easy, simple and small task to prove that it's indeed plagiarism, or at least the case would be so probable that it can be assumed as plagiarisms until proven otherwise(sometimes even beyond reasonable doubt), all due to the fact that making the same rare programming pitfalls when trying to do the same things is effectively a black swan.
On a side note: This also implies that, sometimes prioritizing plugin development speed over code quality can surprisingly help countering plagiarisms, as scammers will be harder to hide their plagiarisms in those unintended cases.

As a bonus, the plugins written this way can also be so apparently(but not actually if you know the "secret") cryptic and fragile that others can't change anything meaningful without breaking at least something else significant(but not so cryptic that plugin users can't report meaningful errors), so those trying to hide their plagiarisms will instead keep encountering serious bugs after serious bugs, therefore they'll eventually just give up plagiarizing or change to plagiarizing without hiding(and will therefore be caught very quickly).
On the other hand, the plugin developers can keep some private test suites and documentation files(of course maintaining them needs tons of extra work) that can help them work well with those apparently cryptic and fragile codes, and let the sufficiently trustworthy fellows to have somewhat controlled, limited and recorded accesses on them in case they've to solve some compatibility issues and need such accesses.

Of course, in order to keep the negative side-effects under control, at least the following conditions must be met:
1. The plugins will still keep working well on the user level without much nontrivial bugs that can't be fixed quickly(it needs the plugin developers to keep tons of codes inside their heads)
2. The plugin developers will still keep working with the plugins effectively and efficiently despite the extremely low code qualities(private test suites can help a lot here in the long term)
3. At least some fellow plugin developers will still want to address compatibility issues with said plugins even though it'd be a hellish nightmare in general(provided that those compatibility issues must be addressed on their sides)
Otherwise those plugins won't be plagiarized not because doing so would be too hard without being caught, but because there would be so few users using them that it's simply not worth plagiarizing them.

Also, bad junior programmers like me, those having next to no plugin users or those not being harassed by plagiarisms that are hard to prove shouldn't even think about actually doing this, because:
1. If you're not at least about to be a seasoned professional software engineer, it's almost certain that deliberately writing uncommon bad codes with strange poor designs will shoot yourself in your foot so hard that your plugins won't even work at all
2. If you've next to no plugin users, it probably means your popularity and reputations are so low that nearly no scammer will think that your plugins will be worth plagiarizing(there will always be exceptions though and I'm one of the few), because even the most brazen plagiarisms demand "actual work" in the views of those scammers(otherwise no one would be shameless enough to sell others' plugins without permissions using "charging for searching services" as a pathetic excuse)
3. If all you've faced are obvious plagiarisms, then the whole idea here will only do harm to everyone except the scammers and no good to anyone, as those scammers would just continue to make obvious plagiarisms anyway
In short, the whole approach might only payoff for very proficient plugin developers having tons of plugins, popularity, reputations and users, especially when they're already being severely harassed by plagiarisms that are all very hard to prove otherwise.

While I feel bad for those being harassed by plagiarisms that are hard to prove on a regular basis, I'd like to know what you'll think about incorporating this idea with your existing plagiarism counters, as I'm wondering whether this additional option can indeed further help stopping such plagiarisms :)

State resistance based on number of turns or effect magnitude instead of chance

Let's throw out 1 more example: hp/mp sap(poison) states in an ATB system, where something like 100hp/mp is reduced per second and the state turn will also be reduced per second(of course, the second's combined by all ATB frame updates, so time spent on inputting actions with the full wait ATB mode doesn't count).
In this case, maybe it makes sense for such a state resistance to be based on turns, so battlers having that state may have its turn reduced to say, 30 from 60.
Of course, reducing the magnitude from 100 to 50 as a form of state resistance might also work well here :)

State resistance based on number of turns or effect magnitude instead of chance

In the default RMVXA and RMMV(I don't know if it's the same for the others as well), the state resistance's in the form of decreasing the chance to inflict a state, but without any other change if the state's still inflicted.
I personally feel that it can be excessively and unnecessarily random, especially for states lasting for several turns and with serious effects.
Therefore, my very naive idea is that, instead setting the state resistance as decreasing the chance to inflict a state, the former should be set as decreasing the number of turns of the inflict state, thus eliminating the randomness completely.

Let's say a state reducing a battler's atk by 50% for 3 turns, which is of course a very serious deal.
But there are some battlers having certain resistance on that state, some may reduce the number of turns to 2, and some may even reduce the number of turns to 1, thus removing the situation of a gamble between having no effect and reducing atk by 50% for 3 turns.

Similarly, state resistance can also be implemented as reducing the state effect magnitude, and in the above case, like reducing it to 25% from 50%.

So what do you think about state resistance? Do you prefer randomness or determinism in this case?

[SCRIPTING] [RMMV] Your experiences/thoughts on writing automated tests for plugins?

Lately I've been trying to write some automated tests to reduce the chance of my plugins to have nontrivial bugs unnoticed. This helped me catch quite some issues that I'd have missed for a long time otherwise, especially when developing a advanced complex battle system plugin. Of course, I still have to debug and dry-run my code to reason about my codebase, but automated tests can help me as well.

For instance, if my plugin relies on some invariants and I accidentally break some of them when adding some new features, those automated tests(which catch regressions in this case) might be able to inform me about that immediately, so I can fix the issues more effectively and efficiently.

Similarly, if my plugin has some compatibility issues with some other plugins and there are automated tests that can inform what assumptions from which plugins are violated(even though some of them should be violated in some cases), I can find the root causes more quickly.

Another useful scenario is to check inputted parameters/added notetags from plugin users to see if the contents are valid, even though it's more like assertions than automated tests. With a well-presented error log to plugin users(assuming that they do enable those tests), they might even be able to fix those invalid parameter/notetag values themselves without asking for the others for help.

Unfortunately, it seems to me that it's quite challenging to write testable high quality codes when developing plugins, especially with high test coverage(be it from unit tests, functional tests, integration tests, etc). For instance, I find it uneasy when trying to write many pure functions with well-defined inputs and outputs to increase testability while still conforming to the default RMMV codebase architecture and style, use dependency injections to facilitate swapping between real and test objects without tearing the plugin business logic apart, or writing automated tests that clearly specifies the preconditions and postconditions of functions solely for well-defined side effects without causing the tests to be tautological. Maybe that's just because I'm still inexperienced in writing testable codes in general :)


While some manual testings are inevitable(apart from the obvious parts like compatibility issues and how the plugin user would feel/think when using the plugins, which can only be tested manually), I still want to at least try to have a higher test coverage, like 80%, which is high enough for me in most cases. Maybe it's because I'm still a very bad junior programmer and don't know how to write tests, but right now the best I can get is 20%, which might be enough in some cases but still unsatisfactory for me(I've made a 10K LoC vanilla ES6 pet project via TDD and reached 80% test coverage, even though that's still nothing to brag for).

Strictly speaking, I guess I can get 80% test coverage with the heavy use of heavy mocking, like mocking a whole battler and even the whole battle scene, but I just don't feel that the return of investment of this approach's worth it in most cases. After all, setting up test fixtures and cleaning up mocks with quite some shared mutable states and implicit stateful dependencies can mean tons of tedious work that can be done all wrong way too easily(Not to mention the dedication needed to maintain such tests). Also, the time needed to run such tests might be rather long, but maybe that's just because I don't know how to mock properly yet.

Therefore, I'd like to know if anyone has any success in writing automated tests when developing plugins, and how you managed to get there(be it test after, test first, test driven development, behavior driven development, etc). Alternatively, if you managed to usually develop plugins effectively and efficiently, even including advanced complex ones like complicated and convoluted battle systems with lots of new features, with almost no nontrivial bugs, but nearly without the use of automated tests, I'd like to know how(like writing codes that are so easy, simple and small that no testings are needed? But how so for advanced complex plugins?) :D

DoubleX RMMV Skill Hotkeys

Updates
*      v1.01a(GMT 1400 17-7-2019):
 *      1. Lets you show skill names instead of icons in the hotkey list
 *      2. Lets you show hotkey icons instead of names in the hotkey list
 *      (v1.01a+ will be incompatible with v1.00a due to hotkeyNames changes)

DoubleX RMMV Status Bars

Updates
*      v1.01b(GMT 1200 7-7-2019):
 *      1. Fixed the invisible status bars not being able to be visible again
 *      2. Fixed the equips, classes and actors status bar data not being used

[RMVX ACE] [SCRIPTING] DoubleX Reflect State

I've just updated the script. Please download it again :)

[RMVX ACE] [SCRIPTING] DoubleX Reflect State

Would you mind sending me your project so I can investigate?