Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4.0 #95

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open

v4.0 #95

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
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ build
roblox.toml
*.rbxmx
*.tgz
/dist
/luau/artefacts
/roblox/dist
/roblox/luau/artefacts
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[typescriptreact]": {
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<img src="logo.png" align="right"/>

<h1>RbxNet</h1>
<h3>Advanced multi-language networking framework for Roblox.</h3>
<h1>NET</h1>
<h3>Advanced multi-language networking library powering Roblox games<!-- ;) -->, using the Network Object Model (NOM)</h3>

<a href="https://www.npmjs.com/package/@rbxts/net"><img src="https://badge.fury.io/js/@rbxts%2Fnet.svg" alt="npm version" height="18"></a>
<a href="https://wally.run/package/vorlias/net"><img src="https://img.shields.io/badge/wally%20package-3.0.4-red" height="18"/></a>

---

RbxNet is a definition-based _networking framework_ for Roblox, built in TypeScript, but also available in Luau. It simplifies the creation and management of networking in Roblox experiences.
Net is a definition-based _networking framework_ for Roblox, built in TypeScript, but also available in Luau. It simplifies the creation and management of networking in Roblox experiences.

## Features
- Create a _definitions_ file of all your networking-based objects in your game. This is a single source of truth for your networking objects. No more tracking instances manually, or having to programmatically create them manually. Each network object is represented by an identifier you define, and by default is created on the server at runtime.
Expand Down
63 changes: 63 additions & 0 deletions docs/blog/2024-05-04-version4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
slug: 4.0-release
title: v4.0
author: Vorlias
author_url: https://github.com/vorlias
author_image_url: https://avatars0.githubusercontent.com/u/10781792?s=400&v=4
tags: [update]
---

Welcome to the release of v4.0 of Net. This is a major update from v3.0 and has some design changes from the previous version of Net.

## What's changed
- Net now uses a "builder" model for describing a Network Object Model (NOM) - whereas before you'd do:
```ts title="Net v3.0 Declarations"
import Net from "@rbxts/net";
import t from "@rbxts/t";
export = Net.Create({
TestServerAsyncFunction: Net.Definitions.ServerAsyncFunction<[name: string], boolean>(),
TestEventWithTypeChecking: Net.Definitions.ClientToServerEvent<[text: string]>([
Net.Middleware.TypeChecking(t.string)
]),
TestRateLimitWithTypeChecking: Net.Definitions.ClientToServerEvent([
Net.Middleware.TypeChecking(t.string),
Net.Middleware.RateLimit({ RequestsPerWindow: 5, RequestWindow: NetDuration.minutes(1) }),
// no return checks!! D:
]),
});
```

```ts title="Net v4.0 Network Object Model"
import Net from "@rbxts/net";
import t from "@rbxts/t";
export = Net.Create()
.AddServerOwned("TestServerAsyncFunction", Net.Remote<[name: string]>().WhichReturnsAsync<boolean>())
.AddClientOwned("TestEventWithTypeChecking", Net.Remote<[text: string]>(t.string))
// Much simpler! :D
.AddClientOwned(
"TestRateLimitWithTypeChecking",
Net.Remote(t.string)
.WithRateLimit({ RequestsPerWindow: 5, RequestWindow: NetDuration.minutes(1) })
.WhichReturnsAsync(t.boolean)
)
.Build();
```

## New Additions
- Added `Net.BuildDefinition()` - This will return a Network Object Model factory, which then has the following methods under it:
- `AddServerOwned(id: string, nob: NetworkObject)` - Will add the specified remote as owned by the server (the server invokes events, or handles callbacks for functions)
- `AddClientOwned(id: string, nob: NetworkObject)` - Will add the specified remote as owned by the client (the client invokes events, or handles callbacks for functions)
- `AddNamespace(id: string, nom: NetworkObjectModel)` - Adds a sub-model (scoped) to the parent model
- `SetConfiguration(configuration: Configuration)` - Will set a configuration on this Network Object Model
- `Build()` - Will construct the Network Object Model to actual network objects you can use with the Net API
- Added `Net.Remote()` - This will return a builder for a remote object, and by itself will be a unit network event - and has the following "augmentations":
- `WhichReturnsAsync<T>(check?: Check<T>)` - Specifies this remote is an `AsyncFunction` which returns `T` - You can also add a runtime type check as the argument
- `AsUnreliable()` - Specifies that the remote is an unreliable event (events by default are _reliable_)
- `WithArgumentTypes(...typeChecks: Check[])` - using a type checker such as [t](https://github.com/osyrisrblx/t), you can have explicit type checking. You can also pass these directly to `Net.Remote(...typeChecks: Check[])` to give runtime type checking
- Added `Net.Broadcaster<T>(check: Check<T>)` - Similar to a `Net.Remote` - but server-only and used for server-to-server messaging
- Added a _Mock Context_, this means that Net should work in test environments

## Changes since v3.x
With the above changes, the old legacy API is still currently accessible under `BuildDefinition(definitions: RemoteDefinitions)` - and old definitions can be added via `AddLegacyDefinitions`.

## Removals
84 changes: 64 additions & 20 deletions docs/docs/definitions/00-overview.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
---
id: overview
title: Getting started
sidebar_label: Getting started
slug: /definitions
title: Getting Started
sidebar_label: Getting Started
slug: /NOM/getting-started
---
import Code, { DEFAULT_VALUE, GROUP, TABS } from '@site/src/components/Code'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<!-- Below is an example of a traditional sort of structure a roblox game might have with remotes

![](/img/traditional_remotes.png) -->


## What are definitions?
Definitions are a sort of "blueprint" for all the networking related objects in your game or library.
## What is a Network Object Model? (NOM) 🐹
The Network Object Model is a sort of "blueprint" for all the networking related objects in your game or library.

It's a single source of truth in which all the code in your game can access remotes through, to handle your game or library's networking.

## Creating a definition script
To use definitions, you will need to create a script that is somewhere in `ReplicatedStorage` (or inside the library itself if you're doing it for a library). It needs to be accessible by both server scripts and client scripts.
## Creating the NOM
<!-- To use definitions, you will need to create a script that is somewhere in `ReplicatedStorage` (or inside the library itself if you're doing it for a library). It needs to be accessible by both server scripts and client scripts.

The basic declaration of a definition script, is the following:
The basic declaration of a definition script, is the following: -->

The main way to declare a NOM is to create a root-level [namespace](../API/Namespace):

<Tabs defaultValue={DEFAULT_VALUE} groupId={GROUP} values={TABS}>
<TabItem value="ts">

```ts title="shared/remotes.ts"
import Net from "@rbxts/net";

const Remotes = Net.CreateDefinitions({
// Definitions for the actual remotes will go here
});
const Remotes = Net.BuildDefinition()
.Build();

export = Remotes;
```
Expand All @@ -42,14 +38,62 @@ export = Remotes;
```lua title="src/shared/remotes.lua"
local Net = require(ReplicatedStorage.Net)

local Remotes = Net.CreateDefinitions({
-- Definitions for the actual remotes will go here
})
local Remotes = Net.BuildDefinition()
:Build()

return Remotes
```

</TabItem>
</Tabs>

Then simply, you can import the module from your code, and use the definitions API to get the remotes
- `Net.BuildDefinition()` gives us a NOM builder
- The functions on this are what we'll use to add network objects!
- `Build()` is how we turn the model into objects we can use.

## Contexts

With the returned `Remotes` result - we can access either the _`Server`_ or _`Client`_ contexts (via `Remotes.Server` or `Remotes.Client`)

It's recommended that you create files on the server and client for this:

<Tabs defaultValue={DEFAULT_VALUE} groupId={GROUP} values={TABS}>
<TabItem value="ts">

```ts title="server/remotes.ts"
import Remotes from "shared/remotes";
import ServerRemotes = Remotes.Server;
export = ServerRemotes;
```

</TabItem>
<TabItem value="luau">

```lua title="src/server/remotes.lua"
local Remotes = require(ReplicatedStorage.Remotes)
return Remotes.Server
```

</TabItem>
</Tabs>


<Tabs defaultValue={DEFAULT_VALUE} groupId={GROUP} values={TABS}>
<TabItem value="ts">

```ts title="client/remotes.ts"
import Remotes from "shared/remotes";
import ClientRemotes = Remotes.Client;
export = ClientRemotes;
```

</TabItem>
<TabItem value="luau">

```lua title="src/client/remotes.lua"
local Remotes = require(ReplicatedStorage.Remotes)
return Remotes.Client
```

</TabItem>
</Tabs>
79 changes: 37 additions & 42 deletions docs/docs/definitions/01-starting.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
---
id: starting
title: Creating a definition
sidebar_label: Creating definitions
title: Remotes and the NOM
slug: /definitions/starting
---
import Code, { DEFAULT_VALUE, GROUP, TABS } from '@site/src/components/Code'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

In RbxNet, there are three categories of remote objects:
In RbxNet, there are five categories of remote objects:

- **Event** - This is analogous to a `RemoteEvent`. This is what is used if you want to send an event (like an action) to the server or a player.
- **UnreliableEvent** - This is analogous to `UnreliableRemoteEvent`. Similar to RemoteEvent - however not guaranteed to be recieved.
- **AsyncFunction** - This is _like_ a `RemoteFunction`, but uses `RemoteEvent` internally. The difference with this and `Function` is that `AsyncFunction` _will_ handle time outs and runs completely asynchronously. (meaning it wont yield code) If there is no response from the reciever, it will reject.
- **Function** - This is analogous to a `RemoteFunction`. However unlike a regular `RemoteFunction` this does not allow you to call a client. This is for security reasons discussed [here](https://github.com/roblox-aurora/rbx-net/issues/13)
- [**ExperienceBroadcastEvent**](#todo) - This uses `MessagingService` to send an event to all servers in an experience

## Defining Events & Functions
Given the above knowledge, we can then apply that to our remote definition script. There are the following functions under `Net.Definitions` for creating definitions for the three categories we have above. The API for each type of definition is explicit so it is easy to understand what each defined remote does.
Given the above knowledge, we can then apply that to our remote definition script. The basic way of defining a remote is through the `Net.Remote` function - which will return a builder we can use to further define the shape of our remote object.

### The different types of definitions

- Event
- **`Net.Definitions.ServerToClientEvent`** - Defines an event in which the server sends an event to one or many clients
- **`Net.Definitions.ClientToServerEvent`** - Defines an event in which the client send events to the server
- **`Net.Definitions.BidirectionalEvent`** - Defines an event in which both the server can send an event to one or many clients, and also the clients can send events to the server. _This should only be used in cases where it's required_.

- AsyncFunction
- **`Net.Definitions.ServerAsyncFunction`** - Defines an async function which exists on the server, and can be called by clients. The returned result will be recieved on the client as a promise.
- **`Net.Definitions.ClientAsyncFunction`** - Defines an async function which exists on the client, and can be called by the server. The returned result will be recieved on the server as a promise.
- Function
- **`Net.Definitions.ServerFunction`** - Defines a synchronous function which exists on the server, and can be called by clients

- Broadcast
- **`Net.Definitions.ExperienceBroadcastEvent`** - Defines an event that the server can use to communicate with other servers in the same experience
- Event: `Net.Remote()`
- AsyncFunction: `Net.Remote( ...argumentTypeChecks ).WhichReturnsAsync( returnTypeCheck )`
- Function: `Net.Remote( ...argumentTypeChecks ).WhichReturnsSync( returnTypeCheck )` (only works with server-owned)
- UnreliableEvent: `Net.UnreliableRemote(...argumentTypeChecks)` or `Net.Remote(...argumentTypes).AsUnreliable()`
- ExperienceBroadcastEvent: `Net.Broadcaster(argumentTypeCheck)`

### Defining remotes

Expand All @@ -44,17 +35,19 @@ With the above knowledge, we can create a few example definitions. Say I would l

```ts title="shared/remotes.ts"
import Net, { Definitions } from "@rbxts/net";

const Remotes = Net.CreateDefinitions({
GetPlayerInventory: Definitions.ServerAsyncFunction<() => SerializedPlayerInventory>(),
GetPlayerEquipped: Definitions.ServerAsyncFunction<() => SerializedPlayerEquipped>(),

PlayerInventoryUpdated: Definitions.ServerToClientEvent<[event: InventoryUpdatedEvent]>(),
PlayerEquippedUpdated: Definitions.ServerToClientEvent<[event: EquippedUpdatedEvent]>(),

PlayerUnequipItem: Definitions.ClientToServerEvent<[itemId: number]>(),
PlayerEquipItem: Definitions.ClientToServerEvent<[itemId: number]>(),
});
import t from "@rbxts/t";

const Remotes = Net.BuildDefinition()
// Server async functions
.AddServerOwned("GetPlayerInventory", Net.Remote().WhichReturnsAsync<SerializedPlayerInventory>())
.AddServerOwned("GetPlayerEquipped", Net.Remote().WhichReturnsAsync<SerializedPlayerEquipped>())
// Server events
.AddServerOwned("PlayerInventoryUpdated", Net.Remote<[event: InventoryUpdatedEvent]>(InventoryUpdatedEvent))
.AddServerOwned("PlayerEquippedUpdated", Net.Remote<[event: InventoryUpdatedEvent]>(EquippedUpdatedEvent))
// Client events
.AddClientOwned("PlayerUnequipItem", Net.Remote<[itemId: number]>(t.number))
.AddClientOwned("PlayerEquipItem", Net.Remote<[itemId: number]>(t.number))
.Build();

export = Remotes;
```
Expand All @@ -64,22 +57,24 @@ export = Remotes;

```lua title="src/shared/remotes.lua"
local Net = require(ReplicatedStorage.Net)

local Remotes = Net.CreateDefinitions({
GetPlayerInventory = Net.Definitions.ServerFunction(),
GetPlayerEquipped = Net.Definitions.ServerFunction(),

PlayerInventoryUpdated = Net.Definitions.ServerToClientEvent(),
PlayerEquippedUpdated = Net.Definitions.ServerToClientEvent(),

PlayerUnequipItem = Net.Definitions.ClientToServerEvent(),
PlayerEquipItem = Net.Definitions.ClientToServerEvent(),
})
local t = require(ReplicatedStorage.t)

local Remotes = Net.BuildDefinition()
-- Server async functions
:AddServerOwned("GetPlayerInventory", Net.Remote():WhichReturnsAsync(SerializedPlayerInventory))
:AddServerOwned("GetPlayerEquipped", Net.Remote():WhichReturnsAsync(SerializedPlayerEquipped))
-- Server events
:AddServerOwned("PlayerInventoryUpdated", Net.Remote(InventoryUpdatedEvent))
:AddServerOwned("PlayerEquippedUpdated", Net.Remote(EquippedUpdatedEvent))
-- Client events
:AddClientOwned("PlayerUnequipItem", Net.Remote(t.number))
:AddClientOwned("PlayerEquipItem", Net.Remote(t.number))
:Build();

return Remotes
```

</TabItem>
</Tabs>

Straight away you can see it's quite easy to know what remote does what.
You may notice that we have `SerializedPlayerInventory`, `SerializedPlayerEquipped`, `InventoryUpdatedEvent` and `EquippedUpdatedEvent`. These are [_type guards_](#guards). By using these, we ensure that our networking objects send and recieve the correct types! - this is especially useful at runtime when malicious clients could send incorrect data.
4 changes: 2 additions & 2 deletions docs/docs/definitions/02-implementation.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
id: implementing
title: Using your definitions
sidebar_label: Using definitions
title: Using Remotes with the NOM
# sidebar_label: Using definitions
slug: /definitions/using
---
import Code, { DEFAULT_VALUE, GROUP, TABS } from '@site/src/components/Code'
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/install-lua.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
id: install-luau
title: Install for Luau
title: Install for Roblox (Luau)
---
Depending on your stack for Roblox, you might want to either use the [Rojo](#via-rojo) step or the [Studio](#via-studio) step. If you don't know what _Rojo_ is, use the Studio step.

Expand All @@ -15,7 +15,7 @@ Then, in your `wally.toml` file for your project, put under `dependencies`:
# ...
[dependencies]
# ...
Net = "vorlias/net@3.0.1"
Net = "vorlias/net@4.0.0"
```

Then run
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/install.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
id: install
title: Install for TypeScript
title: Install for Roblox (TypeScript)
---
The TypeScript version of RbxNet requires knowledge and usage of [roblox-ts](https://roblox-ts.com).

Expand Down
6 changes: 6 additions & 0 deletions docs/docs/types/async-function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
id: async-function
title: Async Functions
# sidebar_label: ExperienceBroadcastEvent
slug: /API/AsyncFunction
---
6 changes: 6 additions & 0 deletions docs/docs/types/event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
id: event
title: Events
sidebar_label: Normal Events
slug: /API/Event
---
6 changes: 6 additions & 0 deletions docs/docs/types/experience-event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
id: experience-event
title: Server to Server Messaging
sidebar_label: Server to Other Servers
slug: /API/ExperienceBroadcastEvent
---
Loading