Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mixu78 committed Jan 17, 2021
0 parents commit 02d7556
Show file tree
Hide file tree
Showing 11 changed files with 527 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"jsx": true,
"useJSXTextNode": true,
"ecmaVersion": 2018,
"sourceType": "module",
"project": "./tsconfig.json"
},
"plugins": [
"@typescript-eslint",
"roblox-ts",
"prettier"
],
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:roblox-ts/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"rules": {
"prettier/prettier": [
"warn",
{
"semi": true,
"trailingComma": "all",
"singleQuote": false,
"printWidth": 120,
"tabWidth": 4,
"useTabs": true
}
]
}
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/node_modules
/out
/include
*.tsbuildinfo
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"roblox-ts.vscode-roblox-ts",
"dbaeumer.vscode-eslint"
]
}
13 changes: 13 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
"[typescriptreact]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
"eslint.run": "onType",
"eslint.format.enable": true,
"typescript.tsdk": "node_modules/typescript/lib"
}
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# @rbxts/net-eventemitter

[![NPM](https://nodei.co/npm/@rbxts/net-eventemitter.png)](https://npmjs.org/package/@rbxts/net-eventemitter)

Node.js-inspired event emitter class. Also typesafe.

## Installation
```npm i @rbxts/net-eventemitter```

## Usage
First make an object with ```eventName: readonly t.check<any>[]``` pairs, for example
```ts
import { t } from "@rbxts/t"
const GameEvents = {
roundStart: [t.string, t.number] as const,
roundEnd: [t.string] as const,
}
```
Then create an emitter like so:
```ts
import { ServerNetworkEmitter as Emitter } from "@rbxts/net-eventemitter"
const Emitter = new EventEmitter(GameEvents);
```
where GameEvents is your event object.

To handle any wrong type arguments sent by players pass in a function of type ```(eventName: string, player: Player, args: unknown[]) => void``` to the constructor.

## Example
```ts
import { ServerNetworkEmitter as Emitter } from "@rbxts/eventemitter";

const Events = {
playerDead: [t.string]
}

const invalidArgsHandler = (event, player, args) => {
print(`Player ${player.Name} sent invalid arguments to event ${event}!`)
}

const Emitter = new EventEmitter(Events, invalidArgsHandler);

Emitter.emit("playerDead", "Mixu_78");
```
```ts
import { ClientNetworkEmitter as Emitter } from "@rbxts/eventemitter";

const Events = {
playerDead: [t.string]
}

const Emitter = new EventEmitter(Events);

Emitter.on("playerDead", (player) => print(`${player} died!`))
```
40 changes: 40 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@rbxts/net-eventemitter",
"version": "1.0.0",
"description": "@rbxts/eventemitter, but with 100% more RemoteEvents",
"main": "out/init.lua",
"scripts": {
"prepublishOnly": "rm -r out && rbxtsc --type plugin",
"test-setup": "rm -r ./out && rbxtsc --type game && rojo build -o Tests.rbxlx",
"test": "npm run test-setup && run-in-roblox --place ./Tests.rbxlx --script ./out/testRunner.server.lua"
},
"keywords": [],
"author": "Mixu_78",
"license": "ISC",
"publishConfig": {
"access": "public"
},
"types": "out/index.d.ts",
"devDependencies": {
"@rbxts/compiler-types": "^1.0.0-beta.14.0",
"@rbxts/testez": "^0.3.1-ts.6",
"@rbxts/types": "^1.0.437",
"@typescript-eslint/eslint-plugin": "^4.13.0",
"@typescript-eslint/parser": "^4.13.0",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-roblox-ts": "^0.0.24",
"prettier": "^2.2.1",
"rbxts-transformer-services": "^1.0.0",
"typescript": "^4.1.3"
},
"dependencies": {
"@rbxts/janitor": "^1.0.3",
"@rbxts/services": "^1.1.2",
"@rbxts/t": "^2.1.1"
},
"files": [
"out"
]
}
101 changes: 101 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { ReplicatedStorage, RunService } from "@rbxts/services";
import { Janitor } from "@rbxts/janitor";

import { EventKey, EventsRecord, INetworkClient, TupleToFunction, FolderName } from "./types";

export class ClientNetworkEmitter<Events extends EventsRecord<Events>> implements INetworkClient<Events> {
private static _mock = false;
private eventMap: Map<EventKey<Events>, RemoteEvent>;
private connections: Map<EventKey<Events>, Map<Function, RBXScriptConnection>>;
private janitor: Janitor;

constructor(private events: Events) {
if (RunService.IsServer() && !ClientNetworkEmitter._mock) throw "ClientEmitter cannot be used from the server";
this.eventMap = new Map();
this.connections = new Map();
this.janitor = new Janitor();

this.janitor.Add({
Destroy: () => this.eventMap.clear(),
});
this.janitor.Add({
Destroy: () => {
this.connections.forEach((m) => m.clear());
this.connections.clear();
},
});
}

private getEvent<E extends EventKey<Events>>(eventName: E): RemoteEvent {
if (RunService.IsServer() && !ClientNetworkEmitter._mock) throw "ClientEmitter cannot be used from the server";

const getEvent = () => {
const folder = ReplicatedStorage.WaitForChild(FolderName, 2);
if (!folder) throw "Server side emitter not created!";
const event = folder.FindFirstChild(eventName) as RemoteEvent | undefined;
if (!event) throw "Event not created on server side!";

this.eventMap.set(eventName, event);
return event;
};

const event = this.eventMap.get(eventName) ?? getEvent();

return event;
}
on<E extends EventKey<Events>>(eventName: E, callback: TupleToFunction<Events[E]>): this {
if (!this.connections.has(eventName)) {
this.connections.set(eventName, new Map());
}

if (this.connections.get(eventName)!.has(callback)) throw "Callback already added";

const event = this.getEvent(eventName);

const connection = event.OnClientEvent.Connect(callback);
this.connections.get(eventName)!.set(callback, connection);

this.janitor.Add(connection);

return this;
}
once<E extends EventKey<Events>>(eventName: E, callback: TupleToFunction<Events[E]>): this {
if (!this.connections.has(eventName)) {
this.connections.set(eventName, new Map());
}

if (this.connections.get(eventName)!.has(callback)) throw "Callback already added";

const event = this.getEvent(eventName);

let connection: RBXScriptConnection;
connection = event.OnClientEvent.Connect((...args: unknown[]) => {
(callback as Callback)(...args);
connection!.Disconnect();
connection = undefined!;
});

this.janitor.Add(connection);

return this;
}
off<E extends EventKey<Events>>(eventName: E, callback: TupleToFunction<Events[E]>): this {
this.connections.get(eventName)?.get(callback)?.Disconnect();

return this;
}
fireServer<E extends EventKey<Events>>(eventName: E, ...args: Parameters<TupleToFunction<Events[E]>>): this {
const event = this.getEvent(eventName);
event.FireServer(...args);

return this;
}
Destroy() {
this.janitor.Destroy();

this.connections = undefined!;
this.eventMap = undefined!;
//@ts-expect-error ts doesn't like "this" being reassigned :/
this = undefined!;
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ClientNetworkEmitter } from "./client";
export { ServerNetworkEmitter } from "./server";
Loading

0 comments on commit 02d7556

Please sign in to comment.