Skip to content

Commit

Permalink
Version 1.5.0 (cleanup and documentation)
Browse files Browse the repository at this point in the history
  • Loading branch information
EliteMasterEric committed Feb 22, 2022
1 parent dfe4985 commit 989731b
Show file tree
Hide file tree
Showing 29 changed files with 735 additions and 506 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [1.5.0] - 2022-02-??
### Added
- Added the new `polymod.hscript.PolymodScriptClass` class, which contains utility functions that allow defining custom classes in scripts.
- Added new functionality which allows for parsing and instantiation of classes defined in scripts. See [Scripted Classes](https://polymod.io/docs/scripted-classes) for more information.
- Incorporated the functionality of `hscript-ex` into Polymod.
### Changed
-
- These scripted classes can extend existing classes or even other scripted classes.
- Parse any scripted classes with `registerAllScriptClasses()`, get the list of scripted classes extending a given class with `listScriptClassesExtendingClass()`, then instantiate it with `createScriptClassInstance(className, args)`. You can cast the `AbstractScriptClass` to the appropriate type once instantiated.
- Note this interface is subject to change and may be deprecated in favor of a more seamless interface in the future.
Expand Down
8 changes: 8 additions & 0 deletions docs/_includes/toc.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ <h2>Documentation</h2>
href="{{ '/docs/scripting' | relative_url }}">
Scripting
</a>
<ul>
<li>
<a
href="{{ '/docs/scripted-classes' | relative_url }}">
Scripted Classes
</a>
</li>
</ul>
</li>
<li>
<a
Expand Down
89 changes: 79 additions & 10 deletions docs/docs/scripted-classes.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,81 @@ title: Scripted Classes

See the `openfl_hscript_class` sample for an example of this in action.

This documentation is a WIP because I only just got this working, but here's a quick and dirty rundown:
Scripted classes are classes whose behavior is defined in a script, rather than in source code. They are parsed and registered at runtime, can be instantiated, and otherwise behave almost identically to normal classes.

This is an incredibly powerful feature for quickly prototyping and testing new features, as well as for providing robust and intuitive modding support to your application.

## Creating a Scripted Class

1. Create a class like this:
1. Create a class like this in your project. In our example, we will create a class called ScriptedStage which extends Stage; this will allow scripts to create classes which extend `Stage`.

```
```haxe
@:hscriptClass
class ScriptedStage extends Stage implements HScriptable {
class ScriptedStage extends Stage implements HScriptable {}
```

Note that the body of the class is COMPLETELY EMPTY. This is because the contents are populated at compile time using a macro.

2. Initialize Polymod as normal.
3. Call `ScriptedStage.listScriptClasses()` to get a list of all the classes that have been registered.
- These include any classes defined in script files in your `assets` folder that have the correct file extension (defaulting to `.hxc`).
- You can add additional classes or override existing classes by including them in Polymod mods.
4. Create a script that defines a class, and add it to the `assets` folder of your project (or to a mod folder to be loaded by Polymod).
5. Call `ScriptedStage.init(stageClassName, ...args)` to instantiate an object of the scripted class.
- The `stageClassName` is the name of the class you want to instantiate. It should be one of the classes returned by `ScriptedStage.listScriptClasses()`.
- The `init()` function will also require any constructor arguments that the superclass needs to be instantiated, so they can be passed to the scripted class.
- The return value of `init()` will be of the type `ScriptedStage`. It has full support for compile completion and can be passed as an argument to functions which expect a `Stage` object.

## Example of a Scripted Class

Here is an example of what a user-provided scripted class may look like:

```haxe
// Make sure to import the target class...
import stage.Stage;
// ...and any modules you want to use.
import openfl.utils.Assets;
import openfl.display.Bitmap;
// Extend the Stage class, not the ScriptedStage class.
class BasicStage extends Stage {
// You can define a constructor which utilizes the same arguments as the superclass,
// or fewer arguments if you provide them yourself..
public function new() {
super('basic');
// You can get and set fields of the stage and the program can access those properties.
stageName = 'Basic Stage';
}
// You can define override functions which replace the superclass's behavior completely.
public override function create():Void {
// You can also call the superclass function.
super.create();
// You can call static functions of modules you import, or instantiate classes from those modules.
var landscapeBg = new Bitmap(Assets.getBitmapData('img/stage/landscape.png'));
// You have full access to any properties and methods of objects you instantiated,
// as though you were writing source code directly!
landscapeBg.x = 0;
landscapeBg.y = 0;
landscapeBg.width = 480;
landscapeBg.height = 360;
this.addChild(landscapeBg);
}
// You can also define new functions which can be called from within the script,
// and variables which those functions can use.
var abc:Int = 123;
function coolStuff() {
abc += 10;
if (abc > 1000) {
abc = 20;
}
trace('Value is: ' + abc);
}
}
```

Expand All @@ -24,15 +90,18 @@ class ScriptedStage extends Stage implements HScriptable {
There are many things which you can do within a scripted class, including but not limited to:

* Override the constructor (using `public function new() { ... }`)
* Import modules and instantiate objects (like `import flixel.FlxG`).
* Access public or private fields of the superclass, including functions.
* Define new fields and functions and call them from other functions.
* Modify public or private values of the superclass, and call superclass functions.
* Override existing functions of the class to replace (or extend) functionality.
* Import modules and call static functions or instantiate objects (like `import flixel.FlxG` or `import flixel.FlxSprite`)

There are some things to watch out for though:

* You can't override `static` functions in a script.
* You can't override `static` functions from a script.
* You can't override a function which uses a private class as an argument.
* This is not possible in any Haxe program so don't worry about this.
* You can't (CURRENTLY) override functions with an optional argument like `function create(?name:String = 'test')`
* You can't use string interpolation from a script.
* For example, `trace('Value is ${abc}')` will actually print `Value is ${abc}` instead of `Value is 123`.
* Thankfully, you can still use string concatenation, for example, `trace('Value is ' + abc)`.
* You can't (CURRENTLY) override functions which utilize a constrained generic type like `function test<T:Iterable<String>>(a:T)`
* I think fully generic types work? Haven't tested.
* Sometimes using `this.xyz` to access fields doesn't work, but just `xyz` does.
* You can't (CURRENTLY) override functions with an optional argument like `function create(?name:String = 'test')`
9 changes: 1 addition & 8 deletions polymod/Polymod.hx
Original file line number Diff line number Diff line change
Expand Up @@ -278,21 +278,14 @@ class Polymod
// Store the params for later use (by loadMod, unloadMod, and clearMods)
prevParams = params;

// Do scripted class initialization now that the assetLibrary is
#if hscript_ex
// Do scripted class initialization now that the assetLibrary is loaded.
if (params.useScriptedClasses) {
Polymod.notice(PolymodErrorCode.SCRIPT_CLASS_PARSING, 'Parsing script classes...');
PolymodScriptClass.registerAllScriptClasses();

var classList = PolymodScriptClass.listScriptClasses();
Polymod.notice(PolymodErrorCode.SCRIPT_CLASS_PARSED, 'Parsed and registered ${classList.length} scripted classes.');
}
#else
if (params.useScriptedClasses) {
Polymod.error(PolymodErrorCode.SCRIPT_NO_INTERPRETER,
'Polymod does not support useScriptedClasses unless the library "hscript-ex" is installed.');
}
#end

return modMeta;
}
Expand Down
26 changes: 13 additions & 13 deletions polymod/PolymodConfig.hx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class PolymodConfig
*/
public static var debug(get, default):Null<Bool>;

static function get_debug()
static function get_debug():Null<Bool>
{
// If the value is null, retrieve the value as a Haxe define.
if (debug == null)
Expand All @@ -38,7 +38,7 @@ class PolymodConfig
*/
public static var rootPath(get, default):String;

static function get_rootPath()
static function get_rootPath():String
{
// If the value is null, retrieve the value as a Haxe define.
if (rootPath == null)
Expand All @@ -58,7 +58,7 @@ class PolymodConfig
*/
public static var useNamespaceInPaths(get, default):Null<Bool>;

static function get_useNamespaceInPaths()
static function get_useNamespaceInPaths():Null<Bool>
{
// If the value is null, retrieve the value as a Haxe define.
if (useNamespaceInPaths == null)
Expand All @@ -76,7 +76,7 @@ class PolymodConfig
*/
public static var scriptExt(get, default):String;

static function get_scriptExt()
static function get_scriptExt():String
{
// If the value is null, retrieve the value as a Haxe define.
if (scriptExt == null)
Expand All @@ -94,7 +94,7 @@ class PolymodConfig
*/
public static var scriptClassExt(get, default):String;

static function get_scriptClassExt()
static function get_scriptClassExt():String
{
// If the value is null, retrieve the value as a Haxe define.
if (scriptClassExt == null)
Expand All @@ -113,7 +113,7 @@ class PolymodConfig
*/
public static var scriptLibrary(get, default):String;

static function get_scriptLibrary()
static function get_scriptLibrary():String
{
// If the value is null, retrieve the value as a Haxe define.
if (scriptLibrary == null)
Expand All @@ -131,7 +131,7 @@ class PolymodConfig
*/
public static var appendFolder(get, default):String;

static function get_appendFolder()
static function get_appendFolder():String
{
// If the value is null, retrieve the value as a Haxe define.
if (appendFolder == null)
Expand All @@ -155,7 +155,7 @@ class PolymodConfig
*/
public static var apiVersionMatch(get, default):Null<SemanticVersionScore> = null;

static function get_apiVersionMatch()
static function get_apiVersionMatch():Null<SemanticVersionScore>
{
// If the value is null, retrieve the value as a Haxe define.
if (apiVersionMatch == null)
Expand All @@ -173,7 +173,7 @@ class PolymodConfig
*/
public static var mergeFolder(get, default):String;

static function get_mergeFolder()
static function get_mergeFolder():String
{
// If the value is null, retrieve the value as a Haxe define.
if (mergeFolder == null)
Expand All @@ -192,7 +192,7 @@ class PolymodConfig
// @:deprecated("Functionality removed, new implementation pending")
public static var modPackFile(get, default):String;

static function get_modPackFile()
static function get_modPackFile():String
{
// If the value is null, retrieve the value as a Haxe define.
if (modPackFile == null)
Expand All @@ -210,7 +210,7 @@ class PolymodConfig
*/
public static var modMetadataFile(get, default):String;

static function get_modMetadataFile()
static function get_modMetadataFile():String
{
// If the value is null, retrieve the value as a Haxe define.
if (modMetadataFile == null)
Expand All @@ -228,7 +228,7 @@ class PolymodConfig
*/
public static var modIconFile(get, default):String;

static function get_modIconFile()
static function get_modIconFile():String
{
// If the value is null, retrieve the value as a Haxe define.
if (modIconFile == null)
Expand All @@ -247,7 +247,7 @@ class PolymodConfig
*/
public static var modIgnoreFiles(get, default):Array<String>;

static function get_modIgnoreFiles()
static function get_modIgnoreFiles():Array<String>
{
// If the value is null, retrieve the value as a Haxe define.
if (modIgnoreFiles == null)
Expand Down
1 change: 1 addition & 0 deletions polymod/backends/PolymodAssetLibrary.hx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class PolymodAssetLibrary
backend.destroy();
}
PolymodScriptClass.clearScriptClasses();
polymod.hscript.HScriptable.ScriptRunner.clearScripts();
}

public function mergeAndAppendText(id:String, modText:String):String
Expand Down
Loading

0 comments on commit 989731b

Please sign in to comment.