Skip to content

Commit

Permalink
Updates to function and namespace registring
Browse files Browse the repository at this point in the history
  • Loading branch information
Vorlias committed Jul 5, 2021
1 parent a8161b0 commit b116e5f
Show file tree
Hide file tree
Showing 17 changed files with 618 additions and 50 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,31 @@ This will install both Zircon, as well as the logging support. It is recommended

This will need to be done on both the _client_ and _server_ to achieve full logging.

All logging done through this can be filtered through the console itself. That's the power of structured logging! ;-)
All logging done through this can be filtered through the console itself. That's the power of structured logging! ;-)

## Registering and using Zircon Commands
Below is an example of how to register a command in Zircon:

```ts
import Zircon from "@rbxts/zircon";
import Log from "@rbxts/log";
Zircon.Server.Registry.RegisterFunction(
new ZirconFunctionBuilder("print_message")
.AddArguments("string")
.Bind((context, message) => Log.Info(
"Zircon says {Message} from {Player}",
message,
context.GetExecutor()
)),
[Zircon.Server.Registry.User]
)
```

This will create a global `print_message` that all players can run.

Then if run in Zircon:

<img src="./assets/Example1.png"/>

The first argument of `RegisterFunction` takes a `ZirconFunctionBuilder` - which is the easiest way to build a function. `AddArguments` takes any number of arguments for types you want, in built types in Zircon you can use a string for. Otherwise you supply the type validator object.
Binary file added assets/Example1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
116 changes: 116 additions & 0 deletions assets/logo_small.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/zircon_small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 24 additions & 23 deletions example/server/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,37 @@ import ZrLuauFunction from "@rbxts/zirconium/out/Data/LuauFunction";
import { ZrInstanceUserdata } from "@rbxts/zirconium/out/Data/Userdata";
import Zircon from "@zircon";
import ZirconPrint from "BuiltIn/Print";
import { ZirconFunctionBuilder } from "Class/ZirconFunctionBuilder";
import delayAsync from "Client/BuiltInConsole/DelayAsync";

Log.SetLogger(Logger.configure().WriteTo(Zircon.Log.Console()).EnrichWithProperty("Version", PKG_VERSION).Create());
Log.SetLogger(
Logger.configure()
.WriteTo(Log.RobloxOutput())
.WriteTo(Zircon.Log.Console())
.EnrichWithProperty("Version", PKG_VERSION)
.Create(),
);

Zircon.Server.Registry.RegisterFunction(ZirconPrint, [Zircon.Server.Registry.User]);
Zircon.Server.Registry.RegisterFunction(
new ZirconFunctionBuilder("kill").AddArguments("player?").Bind((context, player) => {
const target = player ?? context.GetExecutor();
target.Character?.BreakJoints();
Log.Info("Killed {target}", target);
}),
[Zircon.Server.Registry.User],
);

Zircon.Server.Registry.RegisterNamespace(
"localplayer",
{
kill: new ZrLuauFunction((context) => {
const player = context.getExecutor();
if (player) {
player.Character?.BreakJoints();
}
}),
},
Zircon.Server.Registry.RegisterFunction(
new ZirconFunctionBuilder("print_message")
.AddArguments("string")
.Bind((context, message) => Log.Info("Zircon says {Message} from {Player}", message, context.GetExecutor())),
[Zircon.Server.Registry.User],
);

Zircon.Server.Registry.RegisterZrLuauFunction(
"spam",
(context, count) => {
// eslint-disable-next-line roblox-ts/lua-truthiness
const message = context.getInput().toArray().join(" ") || "This is a test message";
if (typeIs(count, "number")) {
for (let i = 0; i < count; i++) {
context.getOutput().write(message);
}
}
},
Zircon.Server.Registry.RegisterFunction(
new ZirconFunctionBuilder("print").Bind((context, ...args) => {
Log.Info(args.map((a) => tostring(a)).join(" "));
}),
[Zircon.Server.Registry.User],
);

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rbxts/zircon",
"version": "0.7.0-alpha.0",
"version": "0.8.0-alpha.0",
"description": "",
"main": "out/init.lua",
"scripts": {
Expand Down
16 changes: 16 additions & 0 deletions src/Class/OptionalValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { StatefulZirconValidator } from "./StatefulZirconValidator";
import { InferTypeFromValidator2, ZirconValidator } from "./ZirconTypeValidator";

export class OptionalValidator<T, U = T> extends StatefulZirconValidator<T | undefined, U | undefined> {
public constructor(private innerValidator: ZirconValidator<T, U>) {
super(innerValidator.Type + "?");
}

public Validate(value: unknown): value is T | undefined {
return this.innerValidator.Validate(value) || value === undefined;
}

public Transform(value: T): U | undefined {
return this.innerValidator.Transform?.(value);
}
}
6 changes: 6 additions & 0 deletions src/Class/StatefulZirconValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ZirconValidator } from "./ZirconTypeValidator";

export abstract class StatefulZirconValidator<T, U = never> implements ZirconValidator<T, U> {
public constructor(public Type: string) {}
public abstract Validate(value: unknown): value is T;
}
90 changes: 90 additions & 0 deletions src/Class/ZirconFunction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import ZrContext from "@rbxts/zirconium/out/Data/Context";
import ZrLuauFunction, { ZrLuauArgument } from "@rbxts/zirconium/out/Data/LuauFunction";
import ZrPlayerScriptContext from "@rbxts/zirconium/out/Runtime/PlayerScriptContext";
import { ZirconFunctionBuilder } from "./ZirconFunctionBuilder";
import { InferArguments, Validator, ZirconValidator } from "./ZirconTypeValidator";

export class ZirconContext {
constructor(private innerContext: ZrContext) {}
public GetExecutor() {
const executor = this.innerContext.getExecutor();
assert(executor);
return executor;
}

public GetOutput() {
return this.innerContext.getOutput();
}

public GetInput() {
return this.innerContext.getInput();
}
}

export class ZirconFunction<V extends readonly ZirconValidator<unknown, unknown>[], R> extends ZrLuauFunction {
public constructor(
private name: string,
private argumentValidators: V,
private zirconCallback: (context: ZirconContext, ...args: InferArguments<V>) => R,
) {
super((context, ...args) => {
// We'll need to type check all the arguments to ensure they're valid
// and transform as appropriate for the user side

let transformedArguments = new Array<defined>();
if (this.argumentValidators.size() > 0) {
for (let i = 0; i < this.argumentValidators.size(); i++) {
const validator = this.argumentValidators[i];
const argument = args[i];
if (validator && validator.Validate(argument)) {
if (validator.Transform !== undefined) {
print("validate", i);
transformedArguments[i] = validator.Transform(argument) as defined;
} else {
print("noValidate", i);
transformedArguments[i] = argument;
}
} else {
// TODO: Error message based on type, argument index etc.
error("Failed at argument" + i);
}
}
} else {
transformedArguments = args as Array<ZrLuauArgument>;
}

/// This is not pretty, I know.
this.zirconCallback(
new ZirconContext(context),
...((transformedArguments as unknown) as InferArguments<V>),
);
});
}

public GetName() {
return this.name;
}

private GetArgumentTypes() {
return this.argumentValidators.map((v) => v.Type);
}

/** @internal */
public RegisterToContext(context: ZrPlayerScriptContext) {
context.registerGlobal(this.name, this);
}

public toString() {
return (
`function ${this.name}(` +
this.GetArgumentTypes()
.map((typeName, argIndex) => `a${argIndex}: ${typeName}`)
.join(", ") +
") { [ZirconFunction] }"
);
}

public static args<V extends readonly Validator[]>(...value: V) {
return value;
}
}
58 changes: 58 additions & 0 deletions src/Class/ZirconFunctionBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
BuiltInValidators,
InferArguments,
InferValidator,
InferValidators,
Validator,
ZirconArgument,
ZirconValidator,
} from "./ZirconTypeValidator";
import { ZirconContext, ZirconFunction } from "./ZirconFunction";

export class ZirconFunctionBuilder<V extends ZirconValidator<any, any>[] = []> {
private validators = new Array<ZirconValidator<any, any>>();
private hasVaradic = false;
public constructor(private name: string) {}

/**
* Adds these arguments to this function
* @param args The arguments
* @returns
*/
public AddArguments<TValidation extends Validator[]>(...args: TValidation) {
if (this.hasVaradic) {
throw `Cannot add argument past varadic argument`;
}

for (const argValidator of args) {
if (typeIs(argValidator, "string")) {
this.validators.push(BuiltInValidators[argValidator]);
} else {
this.validators.push(argValidator);
}
}
return (this as unknown) as ZirconFunctionBuilder<[...V, ...InferValidators<TValidation>]>;
}

public AddArgument<TValidation extends Validator>(type: TValidation) {
return (this as unknown) as ZirconFunctionBuilder<[...V, ...InferValidator<TValidation>[]]>;
}

public AddOptionalArgument<TValidation extends Validator>(type: TValidation) {}

/** @internal */
public AddVaradicArgument<TValidation extends Validator>(arg: TValidation) {
this.hasVaradic = true;

return (this as unknown) as Omit<
ZirconFunctionBuilder<[...V, ...InferValidator<TValidation>[]]>,
"AddArguments" | "AddVaradicArgument"
>;
}

public Bind(fn: (context: ZirconContext, ...args: InferArguments<V>) => void) {
return new ZirconFunction(this.name, this.validators as V, fn);
}
}

new ZirconFunctionBuilder("test").AddArguments("player", "object", "unknown").Bind((context, pl, obj, val) => {});
Loading

0 comments on commit b116e5f

Please sign in to comment.