Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 3 additions & 21 deletions docs/additional-modules/networking/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,8 @@ Flamework's networking is designed with ease of use and convenience in mind.
- Two way communication between the server and client.
- [Middleware](./middleware)
- Modify, delay or drop requests on the receiver before being passed to handlers.
- [Namespaces](./namespaces)
- Organize your remote events and remote functions into groups.
- Automatic type guards
- Declare the event and let Flamework handle the rest ~~(except writing your code)~~!

## Re-exporting
After you've defined your networking events and/or functions, you'll want to export the created `server` and `client` objects in separate files.

If you use the [roblox-ts vscode extension](https://marketplace.visualstudio.com/items?itemName=roblox-ts.vscode-roblox-ts) then the intellisense will hide the server objects on the client and vice-versa.

This will also allow you to hide server-specific configuration (such as generated type guards or middleware) from the client, for additional security.

```ts
// Assumes your GlobalEvents/GlobalFunctions object is in shared/networking.ts
import { GlobalEvents } from "shared/networking";
import { GlobalFunctions } from "shared/networking";

// client/networking.ts
export const Events = GlobalEvents.createClient();
export const Functions = GlobalFunctions.createClient();

// server/networking.ts
export const Events = GlobalEvents.createServer();
export const Functions = GlobalFunctions.createServer();
```

66 changes: 43 additions & 23 deletions docs/additional-modules/networking/remote-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,29 @@ It should be noted that you cannot return a value from an event and the return t
If you want two way communication then you should use [RemoteFunctions](./remote-functions).

```ts
// shared/networking.ts
import { Networking } from "@flamework/networking";

interface ClientToServerEvents {
event(param1: string): void;
myClientToServerEvent(param1: string): void;
}

interface ServerToClientEvents {
event(param1: string): void;
myServerToClientEvent(param1: string): void;
}

// Returns an object containing a `server` and `client` field.
// Returns an object containing the `createServer` and `createClient` fields.
export const GlobalEvents = Networking.createEvent<ClientToServerEvents, ServerToClientEvents>();

// It is recommended that you create these in separate server/client files,
// which will avoid exposing server configuration (including type guards) to the client.
// It is recommended that you call `createServer` and `createClient` on the server and client respectively,
// which will avoid exposing server configuration (including type guards) to the client. See the Using Events section below.
export const ServerEvents = GlobalEvents.createServer({ /* server config */ });
export const ClientEvents = GlobalEvents.createClient({ /* client config */ });
```

### Unreliable Events
Flamework supports specifying [unreliable remote events](https://create.roblox.com/docs/reference/engine/classes/UnreliableRemoteEvent).
These events must still follow any limits specified by Roblox (e.g the 900 byte size limit.)
These events must still follow any limits specified by Roblox (e.g the 1000 byte size limit).

You can specify that an event is unreliable using the `Networking.Unreliable` type.

Expand All @@ -42,55 +43,74 @@ interface ClientToServerEvents {
```

## Using Events
Once you've declared all your events, it's time to use them. You can access your events simply by indexing the [Events](./introduction#re-exporting) object.
Once you've declared all your events, it's time to use them. You can access your events on the server or client by simply indexing the object returned by `createServer` or `createClient` respectively.

```ts
// server/networking.ts
import { GlobalEvents } from "shared/networking";

export const Events = GlobalEvents.createServer();
```

```ts
// client/networking.ts
import { GlobalEvents } from "shared/networking";

export const Events = GlobalEvents.createClient();
```

### Firing Events
Send a request between the server and client.

#### Server
```ts
import { Events } from "server/networking.ts";

// Fire to player(s)
Events.event.fire(player, ...args);
Events.event.fire([player1, player2], ...args);
Events.myServerToClientEvent.fire(player, ...args);
Events.myServerToClientEvent.fire([player1, player2], ...args);

// Fire to all players except
Events.event.except(player, ...args);
Events.event.except([player1, player2], ...args);
Events.myServerToClientEvent.except(player, ...args);
Events.myServerToClientEvent.except([player1, player2], ...args);

// Broadcast
Events.event.broadcast(...args);
// Fire to all connected players
Events.myServerToClientEvent.broadcast(...args);

// Predict, fires server event using player as the sender
Events.event.predict(player, ...args);
Events.myServerToClientEvent.predict(player, ...args);

// Shorthand syntax, equivalent to Events.event.fire
Events.event(player, ...args);
// Shorthand syntax, equivalent to Events.myServerToClientEvent.fire
Events.myServerToClientEvent(player, ...args);
```

#### Client
```ts
import { Events } from "client/networking.ts";

// Fire to server
Events.event.fire(...args);
Events.myClientToServerEvent.fire(...args);

// Predict, fires client event from the client
Events.event.predict(...args);
Events.myClientToServerEvent.predict(...args);

// Shorthand syntax, equivalent to Events.event.fire
Events.event(...args);
// Shorthand syntax, equivalent to Events.myClientToServerEvent.fire
Events.myClientToServerEvent(...args);
```

### Connecting
Connecting to an event returns a RBXScriptConnection which can be used to disconnect the event at any time.

The following example is assuming the code is run on the server. Connecting events on the server and client is identical except that clients do not have an additional `player` parameter.
Connecting events on the server and client is identical except that clients do not have an additional `player` parameter.

#### Server
```ts
// Connect to an event
Events.event.connect((player, arg1) => {
Events.myServerToClientEvent.connect((player, arg1) => {
print(player, arg1);
});

// Disconnect an event connection
const myConnection = events.event.connect(() => {});
const myConnection = Events.myServerToClientEvent.connect(() => {});
myConnection.Disconnect();
```
53 changes: 32 additions & 21 deletions docs/additional-modules/networking/remote-functions.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,85 @@
---
title: Remote Functions
---
RemoteFunctions are for two way communicates between the server and client. This means the sender is able to receive a response from the receiver. Flamework's RemoteFunctions implementation use promises which allow you to avoid any dangerous yields, errors, etc. All requests have a timeout of 10 seconds.
RemoteFunctions are for two way communicates between the server and client. This means the sender is able to receive a response from the receiver. Flamework's RemoteFunctions implementation use promises which allow you to avoid any dangerous yields, errors, etc. All requests have a timeout of 10 seconds.

Whilst it is not recommended, Flamework does support `ServerToClient` remote functions. Flamework avoids common pitfalls by implementing timeouts and cancellation (such as a player leaving) using promises.

## Creation
You can use the `Networking.createFunction` macro to create your network handler. This will contain all your events for both server and client and you can also configure your [middleware](./middleware).
You can use the `Networking.createFunction` macro to create your network handler. This will contain all your functions for both server and client and you can also configure your [middleware](./middleware).

```ts
// shared/networking.ts
import { Networking } from "@flamework/networking";

interface ClientToServerFunctions {
function(param1: string): number;
myClientToServerFunction(param1: string): number;
}

interface ServerToClientFunctions {
function(param1: string): number;
myServerToClientFunction(param1: string): number;
}

// Returns an object containing a `server` and `client` field.
// Returns an object containing the `createServer` and `createClient` fields.
export const GlobalFunctions = Networking.createFunction<ClientToServerFunctions, ServerToClientFunctions>();

// It is recommended that you create these in separate server/client files,
// which will avoid exposing server configuration (including type guards) to the client.
// It is recommended that you call `createServer` and `createClient` on the server and client respectively,
// which will avoid exposing server configuration (including type guards) to the client. See the Using Functions section below.
export const ServerFunctions = GlobalFunctions.createServer({ /* server config */ });
export const ClientFunctions = GlobalFunctions.createClient({ /* client config */ });
```

## Using Functions
Once you've declared all your functions, it's time to use them. You can access your functions simply by indexing the [Functions](./introduction#re-exporting) object.
Once you've declared all your functions, it's time to use them. You can access your functions by simply indexing the object returned by createServer or createClient respectively.

A `player?` parameter in the following examples means that the parameter only exists on the server, and is absent on the client.
```ts
// server/networking.ts
import { GlobalFunctions } from "shared/networking";

export const Functions = GlobalFunctions.createServer();
```

```ts
// client/networking.ts
import { GlobalFunctions } from "shared/networking";

export const Functions = GlobalFunctions.createClient();
```

### Invoking Functions
Invoke a request and wait for a response.

```ts
// Invoke a function
Functions.function.invoke(player?, "my parameter!").then((value) => ...);
Functions.myFunction.invoke("my parameter!").then((value) => ...);

// Shorthand syntax, equivalent to Functions.
Functions.function(player?, "my parameter!").then((value) => ...);
// Shorthand syntax, equivalent to invoke
Functions.myFunction("my parameter!").then((value) => ...);

// Predict, simulates a request being sent
Functions.function.predict(player?, "my parameter!").then((value) => ...);
// Predict, invokes the callback
Functions.myFunction.predict("my parameter!").then((value) => ...);
```

### Handling Functions
You can only connect one handler to each function. Calling `setCallback` more than once will override the existing handler but will result in a warning being outputted.

```ts
// With a normal function
Functions.function.setCallback((player?, param1) => {
print("This is", param1);
Functions.myFunction.setCallback((param1) => {
print(param1);
return math.random(1, 100);
})

// With an async function
Functions.function.setCallback(async (player?, param1) => {
print("This is", param1);
Functions.myFunction.setCallback(async (param1) => {
print(param1);
return await myAsyncNumberGenerator(1, 100);
})
```

## Errors
Flamework's networking exposes a `NetworkingFunctionError` enum which is used whenever a RemoteFunction request is rejected.


| Name | Description |
|---------------|--------------------------------------------------------------------------------|
| Timeout | The request surpassed the timeout length. |
Expand All @@ -77,12 +89,11 @@ Flamework's networking exposes a `NetworkingFunctionError` enum which is used wh
| Unprocessed | The request was not processed by the receiver. |

### Handling errors

Flamework's RemoteFunctions return promises which allows you to handle them the same as any other promise.
Flamework always passes a `NetworkingFunctionError` as the rejection value, which tells you the reason the request failed.

```ts
Events.function.invoke()
Functions.myFunction.invoke()
.then((value) => print("I successfully got", value))
.catch((reason) => {
if (reason === NetworkingFunctionError.Timeout) {
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/lifecycle-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ It runs after physics has been done and is optimal for responding to changes in

The OnPhysics event is a lifecycle event that is directly connected to the RunService.Stepped event.

It runs prior to physics has been done and is optimal for manipulating physics.
It runs prior to physics being done and is optimal for manipulating physics.

## OnRender (Singletons, Components)

Expand Down