Vertex framework (typescript)

Hello everyone, today im releasing my typescript tools that i was using for about a month

Note that this is not a roleplay framework!

contributions are welcome, bug reports with repro steps too

Core package
source code
npm package

Core package provides some useful tools and can be used both on client and server

Note: all the method decorators can be applied to any class, but it will work only with a class that is decorated with Controller decorator.

Examples

Chat commands

// using tools from core package
import { Command } from "./command.decorator";
import { Controller } from "./controller.decorator";

@Controller('T')
class T {
  @Command('chatCommand1', false)
  private chatCommandHandler1(...args: unknown[]): void {
  }

  // 2nd param is optional, false by default
  @Command('chatCommand2', true)
  private chatCommandHandler2(...args: unknown[]): void {
  }
}

// NOT using the core package tools
class T {
  constructor() {
    RegisterCommand('chatCommand1', this.chatCommandHandler1.bind(this), false)
    RegisterCommand('chatCommand2', this.chatCommandHandler2.bind(this), true)
  }

  private chatCommandHandler1(...args: unknown[]): void {}
  private chatCommandHandler2(...args: unknown[]): void {}
}

Exports

//  using this package
import { Export } from "./export.decorator";
import { Controller } from "./controller.decorator";

@Controller('T')
class T {
  // exportName is optional, controllerName_methodName by default
  @Export('exportName')
  private export1(...args: unknown[]): void {}

  @Export()
  private export2(...args: unknown[]): void {}
}

// not using this package
class T {
  constructor() {
    global.exports('exportName', this.export1.bind(this))
    global.exports('T_export2', this.export2.bind(this))
  }

  private export1(...args: unknown[]): void {}

  private export2(...args: unknown[]): void {}
}

Local and Net events

// using this package
import { Controller } from "./controller.decorator";
import { LocalEvent } from "./local-event.decorator";
import { NetEvent } from "./net-event.decorator";

@Controller('T')
class T {
  @LocalEvent('event')
  private eventHandler(...args: unknown[]): void {}
  
  @NetEvent('netEvent')
  private netEventHandler(...args: unknown[]): void { }
}

// not using this package
class T {
  constructor() {
    on('event', this.eventHandler.bind(this))
    onNet('netEvent', this.netEventHandler.bind(this))
  }

  private eventHandler(...args: unknown[]): void {}
  private netEventHandler(...args: unknown[]): void { }
}

Ticks

// using this package
import { Controller } from "./controller.decorator";
import { OnTick } from "./tick.decorator";

@Controller('T')
class T {
  // interval is optional, default 0
  @OnTick(100)
  private tick1(...args: unknown[]): void {}

  @OnTick()
  private tick2(...args: unknown[]): void {}
}

// not using this package
class T {
  constructor() {
    setInterval(this.tick1.bind(this), 100)
    setTick(this.tick2.bind(this))
  }

  private tick1(...args: unknown[]): void {}

  private tick2(...args: unknown[]): void {}
}

// Tick class
import { Tick } from "./tick";

const tick = new Tick(() => {
  console.log('in vehicle')
});

on('enterVehicleEvent', () => tick.start());

on('leaveVehicleEvent', () => tick.stop());


// You can also pause a tick for some amount of time
// The example bellow waits for 1000ms and then starts again
tick.pause(1000)

Client package
source code
npm

This package provides some useful tools for Client side only
It makes easier listetning to nui callbacks and handling keys
Examples

// key handling
import {Controller, Vertex} from "@vxf/core";
import {OnKeyDown} from "./key-down.decorator";
import {OnKeyUp} from "./key-up.decorator";
import {KeyBindReader} from "./key-bind.reader";

@Controller('T')
class T {
  @OnKeyUp('keyboard', 'f1', 'Description')
  private onKeyUp(): void {
    // triggered when key is up after pressing
  }

  @OnKeyDown('keyboard', 'f1', 'Description')
  private onKeyDown(): void {
    // triggered when key is down
  }
}

(() => {
  const app = new Vertex({
    controllers: [T],
    metadataReaders: [KeyBindReader],
  });

  app.start();
})();

// This is pretty much the same
class T2 {
  constructor() {
    RegisterKeyMapping('+keyup', 'Description', 'keyboard', 'f1')
    RegisterCommand('+keyup', this.onKeyUp.bind(this))
    RegisterCommand('-keyup', this.onKeyDown.bind(this))
  }

  private onKeyUp(): void {
    // triggered when key is up after pressing
  }

  private onKeyDown(): void {
    // triggered when key is down
  }
}
// nui callbacks
import {Controller, Vertex} from "@vxf/core";
import {NuiCallback} from "./nui-callback.decorator";
import {NuiReader} from "./nui.reader";

@Controller('T')
class T {
  @NuiCallback()
  private nuiCallback(data: unknown): number {
    return 228;
  }
}

(() => {
  const app = new Vertex({
    controllers: [T],
    metadataReaders: [NuiReader],
  });

  app.start();
})();


// without this package
class T2 {
  constructor() {
    // should be lowercase
    const cbName = 'controllerName/methodName'.toLowerCase();
    global.RegisterNuiCallbackType(cbName);
    global.on?.(
      `__cfx_nui:${cbName}`,
      (data: unknown, cb: (r: unknown) => void) => {
        const result = this.nuiCallback(data);
        if (result instanceof Promise) {
          return result.then(cb)
        }
        cb(result);
      },
    );
  }


  private nuiCallback(data: unknown): number {
    return 228;
  }
}

Remote packages

There is no readme for client side, but its similar to server side remote

Server
npm
github

Client
npm
github

I’ve added examples to svremote package, you may find it useful

Server natives wrapper
well it’s not very similar to client natives wrapper, but it’s easy to use
source code
npm

7 Likes

Nice release, I’ve messed around with your Core package a little bit but wasn’t able to get the command handlers to work as intended.

Also, have you thought about maybe making the controller name parameter option and defaulting to the class name when it’s undefined?

2 Likes

hi there, thx for the feedback
to register commands you have to specify the metadata reader in Vertex constructor
this was made to make the framewrok flexible
example

entry file

import 'reflect-metadata';
import { CommandReader, EventReader, Vertex } from '@vxf/core';
import { InternalEventReader } from '@vxf/internal-events';
import { BootstrapService } from './bootstrap';
import * as controllers from './api/controllers';

(() => {
  const bs = new BootstrapService();
  bs.configureServices();
  bs.configureInjections();
  bs.configureEvents();
  const app = new Vertex({
    controllers: Object.values(controllers),
    metadataReaders: [InternalEventReader, EventReader, CommandReader],
  });
  app.start();
})();

example controller

import { Command, Controller, Inject } from '@vxf/core';
import { IDialogueService, Services } from '../../core';

@Controller('Dialogue')
export class DialogueController {
  @Inject(Services.DIALOGUE)
  private service: IDialogueService;

  @Command('command')
  private commandHandler(): void {
    console.log('hi im a command handler!!');
  }
}

Also, about controller names
i had thoughts about making the name optional, will be implemented in the next release with some minor fixes/changes
Notice that tools like webpack don’t care about classnames

Ahh Yes I see now, I guess I assumed they were just helper decorators and less a complete framework.
However, this reminds me a lot of the way express bootstraps :thinking:
Interesting…

What does this sort of integrated system provide that basic class decorators can not? as in, what’s the added benefit of using your boilerplate code?