Code Monkey home page Code Monkey logo

omp-node-lib's Introduction

Infernus

npm npm npm bundle size

The name "Infernus" comes from the vehicle id 411 in the game.

NodeJS library for scripting Open Multiplayer. Highly recommended to get started with the infernus-starter template.

Installation

pnpm add @infernus/core

Composition

/ Layer Example
1 Application Development GameMode such as freeroam, roleplay
2 Class Wrapper Call functional wrappers by classes
3 Functional Wrapper Such as samp/omp/streamer wrapper
4 Samp Node SDK builds a bridge to the base
5 Omp Server Base

Contributors

Carl You
Carl You

💻
Andonrai
Andonrai

🐛

License

MIT License © 2022-PRESENT Carl You

omp-node-lib's People

Contributors

dockfries avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

omp-node-lib's Issues

Write a simple online document

If I am free to continue to maintain this project, of course, if you are interested, you can also help me.

The internal implementation is actually very close to the way native api is called, as long as you write it by hand.

The difference is that in some small places, such as event splitting, there can be multiple event classes to drive callbacks, which can easily split the logi

Ideas about middleware callback patterns

It's just an experiment, and I don't have the energy to refactor yet.

enum EventSpecialKey {
  MiddlewareReady = '__isReady__'
}

type PromisifyCallbackRetType = number | boolean | Promise<any>

interface Middleware {
  setup?: (...args: any[]) => void;
  updated: (next: () => number, ...args: any[]) => PromisifyCallbackRetType;
  unmounted?: (...args: any[]) => void;
}

type PlayerEventCallback = 'connect' | 'disconnect';

function promisifyCallback(result: PromisifyCallbackRetType, defaultValue: boolean = true) {
  if (result instanceof Promise) {
    return +defaultValue;
  }
  const ret = +result;
  return isNaN(ret) ? +defaultValue : ret;
}

function executeMiddlewares(event: { middlewares: Record<string, Middleware[]> }, callback: string, defaultValue: boolean, ...args: any[]) {
  const middlewares = event.middlewares[callback];

  for (const m of middlewares) {
    if (m.setup && !Reflect.get(m, EventSpecialKey.MiddlewareReady)) {
      m.setup(...args);
      Reflect.set(m, EventSpecialKey.MiddlewareReady, true);
    }
  }

  let index = 0;

  const next = () => {
    index++;
    if (index < middlewares.length) {
      return promisifyCallback(middlewares[index].updated(next, ...args));
    }
    for (const m of middlewares) {
      if (m.unmounted && Reflect.get(m, EventSpecialKey.MiddlewareReady)) {
        m.unmounted(...args);
        Reflect.set(m, EventSpecialKey.MiddlewareReady, false);
      }
    }
    return +defaultValue;
  };

  if (middlewares.length > 0) {
    return promisifyCallback(middlewares[index].updated(next, ...args))
  }
  return +defaultValue;
}

function OnPlayerConnect() {
  // Math.round(Math.random() * 10)
  return executeMiddlewares(PlayerEvent, 'connect', true, 0);
}

function OnPlayerDisconnect() {
  // Math.round(Math.random() * 10)
  return executeMiddlewares(PlayerEvent, 'disconnect', true, 0);
}

class Player {
  name: string = ''
  constructor(public readonly id: number) {
  }
}

class PlayerEvent<T extends Player> {

  static readonly middlewares: Record<PlayerEventCallback, Middleware[]> = {
    'connect': [],
    'disconnect': []
  };

  static readonly players = new Map<number, { context: PlayerEvent<any>, value: Player }[]>();

  constructor(private playerConstructor: new (id: number) => T) {
  }

  private getProxyPlayer(player: T) {
    return new Proxy(player, {
      set(target, p, newValue) {
        PlayerEvent.players.get(player.id)?.forEach(item => {
          const has = Reflect.has(item.value, p);
          const descriptor = Reflect.getOwnPropertyDescriptor(item.value, p)
          const writeable = descriptor && descriptor.writable;
          if (has && writeable) {
            Reflect.set(item.value, p, newValue)
          }
        })
        return true
      },

    })
  }

  private setupMiddleware(playerId: number) {
    let players = PlayerEvent.players.get(playerId);

    if (!players) {
      players = [{ context: this, value: new this.playerConstructor(playerId) }];
      PlayerEvent.players.set(playerId, players);
    }

    let player = players.find(p => p.context === this);
    if (!player) {
      const addPlayer = { context: this, value: new this.playerConstructor(playerId) };
      players.push(addPlayer);
      player = addPlayer;
    }
  }

  private unmountedMiddleware(playerId: number) {
    let players = PlayerEvent.players.get(playerId);

    if (!players) return;

    let playerIdx = players.findIndex(p => p.context === this);

    if (playerIdx === -1) return;

    players.splice(playerIdx, 1);
  }

  onConnect(fn: (next: () => ReturnType<typeof promisifyCallback>, player: T) => PromisifyCallbackRetType) {
    PlayerEvent.middlewares['connect'].push({
      setup: this.setupMiddleware.bind(this),
      updated: (next, playerId: number) => {
        const players = PlayerEvent.players.get(playerId)!;
        const player = players.find(p => p.context === this)!;

        const proxyPlayer = this.getProxyPlayer(player.value as T);
        return fn(next, proxyPlayer);
      }
    })
  }

  onDisconnect(fn: (next: () => ReturnType<typeof promisifyCallback>, player: T) => PromisifyCallbackRetType) {
    PlayerEvent.middlewares['disconnect'].push({
      setup: this.setupMiddleware.bind(this),
      updated: (next, playerId: number) => {
        const players = PlayerEvent.players.get(playerId);

        if (!players) return next();

        const player = players.find(p => p.context === this);

        if (!player) return next();


        const proxyPlayer = this.getProxyPlayer(player.value as T);
        return fn(next, proxyPlayer);
      },
      unmounted: this.unmountedMiddleware.bind(this)
    })
  }
}

const playerEvent = new PlayerEvent(Player);
const playerEvent2 = new PlayerEvent(Player);

playerEvent.onConnect((next, player) => {
  player.name = 'a';
  return next();
})

playerEvent2.onConnect((next, player) => {
  console.log(player)
  return next();
})

playerEvent.onConnect((next, player) => {
  player.name = 'b';
  return next();
})


playerEvent.onDisconnect((next, player) => {
  console.log(player);
  return next();
})


playerEvent.onDisconnect((next, player) => {
  console.log(player);
  next();
  console.log(PlayerEvent.players)
  return true;
})



playerEvent2.onDisconnect((next, player) => {
  console.log(player);
  return next();
})


console.log(OnPlayerConnect());
setTimeout(() => {
  console.log(OnPlayerDisconnect())
}, 1500)

Here are the usage guidelines:

  1. Use a proxy to handle the assignment issue after middleware execution for multiple instances.
  2. "await next()" is not supported, but the middleware supports async functions.
  3. When encountering async in a middleware, it will return the defaultValue set instead of the result after awaiting. It also means that the return value of subsequent middlewares is not important. If all the middlewares are synchronous functions until the last one, and the last one also returns the result of "next()", it will return the defaultValue.
  4. If you don't have a better idea, you can return the result of "next()" after each middleware callback.
  5. Usually, the callbacks for creating and destroying each event instance should be registered and paired. Otherwise, unpredictable accidents may occur, such as the "onConnect" and "onDisconnect" events for a player, or the "onCreated" and "onDestroyed" events for an object.

streamer wrapper for samp-node

The current idea is to package the classes according to the streamer's github wiki subdirectory structure, such as natives/enums/definitions/callbacks, after which the user can package some classes to operate, such as dynamic object classes, define methods for creating, destroying, etc., instead of continuing to call these classes in the traditional functional way native.

add player.fps getter

reference;
stock GetPlayerFPS(playerid) return pFPS[playerid];
new pDrunkLeveLast[MAX_PLAYERS];

new drunknew;
drunknew = GetPlayerDrunkLevel(playerid);
if (drunknew < 100) {
    SetPlayerDrunkLevel(playerid, 2000);
} else {

    if (pDrunkLevelLast[playerid] != drunknew) {

        new wfps = pDrunkLevelLast[playerid] - drunknew;

        if ((wfps > 0) && (wfps < 400))
        pFPS[playerid] = wfps;

        pDrunkLevelLast[playerid] = drunknew;
    }

}

and call GetPlayerFPS(playerid); where you have to.

Textdraws are always white

I'm creating a textdraw, but it doesn't matter what color I put on it, it's always white, and if I try to get the color with getBoxColour(), it returns undefined.

Code used to create the textdraw

Result

pino logger output to file

  1. if the output file location can be specified by the developer
  2. Logger is not currently available in the development layer

Wrapping and improving the original function to be called by class

Such as textdraw, vehicles, players, worlds, by encapsulating classes, writing variables, methods, getter/setter makes programming more elegant.
it is worth noting that for some callback events, for example OnPlayerConnect, player parameters should be written in a more extensible way, for example paradigm modifiers, otherwise other coders cannot inherit classes to derive them. (p: <T extends PlayerClass>)

samp-voice wrapper

Please wait for omp-node, otherwise at this stage samp-node cannot implement samp-voice function wrapper, only can implement callback wrapper

Error creating an object

I am creating an object but it returns undefined and I want to texture.

Code:

exampleObj return undefined

The pickup does not appear

I'm trying to create a pickup, but it doesn't show up in the game

Used code:

Values ​​returned when creating the pickup:

Why you can use "samp" directly instead of importing

Since our mounted samp-node plugin automatically injects samp variables and all event constants into node globals at runtime, we can call it directly.
We can see the global variables via console.log(globals);

develop generators/editors

e.g. textdraw, object, material, attach editors(vehicle, player, object?), store and read using json.

Refer to the library developed by the better known pawn language.

Examples include tde editor and Fusez's Map Editor

pawn.raknet wrapper

Please wait for omp-node, otherwise at this stage samp-node cannot implement raknet function wrapper, only can implement callback wrapper

nex-ac wrapper

It is accurate to use ts syntax to implement nex-ac function, callback, and split by function point and improve scalability.
Now, the inc of nex-ac has thousands of lines, which is hard to understand.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.