npm package: @rbxts/wired
Description: Lightweight Remote Wrapper for Roblox
Author: PepeElToro
Latest version: 0.2.4

# Wired is a lightweight remote wrapper for Roblox

Wired abstracts roblox remotes for you to use them in Roblox-ts offering fully typed remotes you can use in your project!

### Changelog 0.1.0

-   Added `FakeFire(player)` for simulating a player firing a **ClientEvent** for testing purposes
-   Added `FakeCall(player)` for simulating a player calling a **ServerAsync** for testing purposes, returns what the server returned
-   Added `FakeFire()` for simulating the server firing a **ServerEvent** for testing purposes
-   Changed function name from `DefineRemotes()` to `DeclareRemotes()`
-   Added `OnPrepared` signal

## Declaring your remotes

First declare your remotes with `const remotes = DeclareRemotes(name, declaration)`. You might want to export the returned value to be used somewhere else. It's important that the export is available for both Server and Client

```ts
import { DeclareRemotes } from "@rbxts/wired";

export const Remotes = DeclareRemotes("Remotes", {
	//Your declarations here
});

const ServerRemotes = Remotes.Server;
const ClientRemotes = Remotes.Client;
```

We can define as much remotes as we want in different files. This can be useful for modularity, but, depending on your project, creating everything in the same file might be easier to manage

## Constructing the Roblox instances

After we declared our remotes, it's important that we construct the corresponding roblox `RemoteEvent` and `RemoteFunction` instances. For this we are gonna call `Remotes.Server.Construct()` on the Server.
(_If we use DeclareRemotes() on more files, you need to call it on each one_)

```ts
import { Remotes } from ""; //Remote Declarations path

Remotes.Server.Construct();
//Created instances can be found in ReplicatedStorage._wired
```

We can use these declarations before the instances have been constructed and anything we do will be queued up until the instances exist. If an action has been queued up for more than 7 seconds, wired will warn about it.

This is mainly so we can get our remotes at the top of our scripts without wired yielding or erroring if the instances haven't been created. It's recommended to call `Construct()` as soon as possible.

## Replicating Remotes to the Client

When our remotes have been created on the Server, we need the client to wait for these instances before using them. For this we need to call `Remotes.Client.Prepare()` this function returns a `Promise` that resolves when all the instances are ready to be used

```ts
import { Remotes } from ""; //Remote Declarations path

Remotes.Client.Prepare().andThen(() => {
	print("Instances are ready!");
});
//Created instances can be found in ReplicatedStorage._wired
```

Similar to Server, anything we do will be queued up until the instances exist, wired will warn you if it's been waiting for more than 7 seconds. It's recommended to call `Remotes.Client.Prepare` as soon as possible. You can use the returned promise for creating a loading screen

-   **[ WARNING ]**  
    In the **Server** context `Remotes.Client` will be nil, and similarly In the **Client** `Remotes.Server` wil be nil. Roblox-ts wont be able to check this

## Using your remotes

We can use `Remotes.Get(remote: string)` to find a remote
Let's see all the remotes we can create

```ts
import { DeclareRemotes, ServerAsync, ServerEvent, ClientEvent, RemoteEvent } from "@rbxts/wired";

export const Remotes = DeclareRemotes("Remotes", {
	DoubleNumber: ServerAsync<(num: number) => number>(),
	OnDamageTaken: ServerEvent<(damage: number) => void>(),
	OnPlayerLoaded: ClientEvent<() => void>(),

	//First generic is for server, second one for client
	OnMessageSent: RemoteEvent<(from: Player, msg: string) => void, (msg: string) => void>(),
});
```

-   **[ WARNING ]**  
    The generic you give for the remotes is useful for type inference, but wired **will not** typecheck the remotes on runtime. Any check needs to be done by you to avoid exploiters

#### ServerAsync

`RemoteFunction` that clients can call to the server expecting it to return something

```ts
import { Remotes } from ""; //Remote Declarations path

//Server
const DoubleNumber = Remotes.Server.Get("DoubleNumber");

DoubleNumber.SetCallback((player, num) => {
	return num * 2;
});

//Client
const DoubleNumber = Remotes.Client.Get("DoubleNumber");

AsyncFunction.CallServer(5).andThen((double) => print(double)); //10
```

#### ServerEvent

`RemoteEvent` that a server can fire to clients

```ts
import { Remotes } from ""; //Remote Declarations path

//Server
const OnDamageTaken = Remotes.Server.Get("OnDamageTaken");

OnDamageTaken.FirePlayer(player, 50);
OnDamageTaken.FireAll(50);
OnDamageTaken.FireExcept(player, 50);
OnDamageTaken.FireFor((p) => {
	//return true to fire, false to omit
	return p == player;
}, 50);

//Client
const OnDamageTaken = Remotes.Client.Get("OnDamageTaken");

const connection = OnDamageTaken.Connect((damage) => {
	print("Ouch!", damage);
});
connection.Disconnect();
```

#### ClientEvent

`RemoteEvent` that clients can fire to the server

```ts
import { Remotes } from ""; //Remote Declarations path

//Server
const OnPlayerLoaded = Remotes.Server.Get("OnPlayerLoaded");

const connection = OnPlayerLoaded.Connect((player) => {
	print("Player is ready to play!");
});
connection.Disconnect();

//Client
const OnPlayerLoaded = Remotes.Client.Get("OnPlayerLoaded");

game.IsLoaded() || game.Loaded.Wait();
Remotes.Prepare().await();

OnPlayerLoaded.FireServer();
```

#### RemoteEvent

Bidirectional `RemoteEvent` that both the Server and Client can Fire or Connect. Methods like `FireAll`, `FireExcept`, `FireFor` are available here for the server too

```ts
import { Remotes } from ""; //Remote Declarations path

//Server
const OnMessageSent = Remotes.Server.Get("OnMessageSent");

OnMessageSent.Connect((player, msg) => {
	// broadcast to all players except the one who sent the message
	OnMessageSent.FireExcept(player, msg);
});

//Client
const OnMessageSent = Remotes.Client.Get("OnMessageSent");

OnMessageSent.FireServer("Hello!");

OnMessageSent.Connect((player, msg) => {
	print(player.Name, "says:", msg);
});
```

### Unreliable Events

RemoteEvents have their unreliable equivalents. They have the same typings and work the same way, except they use `UnreliableRemoteEvent`s.

`UnreliableClient -> ClientEvent`
`UnreliableServer -> ServerEvent`
`UnreliableEvent -> RemoteEvent`

## Namespaces

Namespaces are useful for grouping your remotes, you can create namespaces directly inside of your remote declarations, and you can create namespaces inside namespaces.

You can separate namespaces in different files as an alternative to calling DeclareRemotes() on different files

```ts
import { DeclareRemotes, Namespace, ServerAsync } from "@rbxts/wired";

export const Remotes = DeclareRemotes("Remotes", {
	ShopRemotes: Namespace({
		BuyRequest: ServerAsync(),
	}),
	GameplayRemotes: Namespace({
		//Remotes here
	}),
	NamespaceRemotes: Namespace({
		SubNamespace1: Namespace({
			//Remotes here
		}),
		SubNamespace2: Namespace({
			//Remotes here
		}),
	}),
});
```

You can access these namespaces with the `Remotes.Namespace()` method

```ts
import { Remotes } from ""; //Remote Declarations path

//Server
const ShopRemotes = Remotes.Server.Namespace("ShopRemotes");
const BuyRequest = ShopRemotes.Get("BuyRequest");
BuyRequest.SetCallback();

//Client
const ShopRemotes = Remotes.Client.Namespace("ShopRemotes");
const BuyRequest = ShopRemotes.Get("BuyRequest");
BuyRequest.CallServer();
```