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

Buffer, no wait, Network...ah, Stream Codecs! #80

Merged
merged 18 commits into from
May 26, 2024
Merged
Changes from 2 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
40 changes: 27 additions & 13 deletions docs/networking/streamcodecs.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ Unless you are manually handling the buffer object, you will generally never cal
| `QUATERNIONF` | `Quaternionf` |
| `GAME_PROFILE` | `GameProfile` |

\* `byte[]` can be limited to a certan number of values via `ByteBufCodecs#byteArray`.
\* `byte[]` can be limited to a certain number of values via `ByteBufCodecs#byteArray`.

\* `String` can be limited to a certan number of characters via `ByteBufCodecs#stringUtf8`.
\* `String` can be limited to a certain number of characters via `ByteBufCodecs#stringUtf8`.

Additionally, there are some static instances that encode and decode primivites and objects using a different method.

Expand All @@ -60,11 +60,15 @@ Additionally, there are some static instances that encode and decode primivites

#### Variable-Sized Number

`VAR_INT` and `VAR_LONG` are alternatives of `INT` where the value is encoded to be as small as possible. This is done by encoding seven bits at a time, using the upper bit as a marker of whether there is more data for this number. Numbers between 0 and 2^28-1 for integers or 0 and 2^56-1 for longs will be sent shorter or equal to the number of bytes in a integer or long, respectively. If the values of your numbers are normally between this range, these variable stream codecs should be used.
`VAR_INT` and `VAR_LONG` are stream codecs where the value is encoded to be as small as possible. This is done by encoding seven bits at a time, using the upper bit as a marker of whether there is more data for this number. Numbers between 0 and 2^28-1 for integers or 0 and 2^56-1 for longs will be sent shorter or equal to the number of bytes in a integer or long, respectively. If the values of your numbers are normally in this range and generally at the lower end of it, then these variable stream codecs should be used.

:::note
`VAR_INT` is an alternative for `INT`.
:::

#### Trusted Tags

`TRUSTED_TAG` and `TRUSTED_COMPOUND_TAG` are variants of `TAG` and `COMPOUND_TAG`, respectively, that have an unlimited heap to decode the tag to, compared to the 2MiB limit of `TAG` and `COMPOUND_TAG`. Only the [block entity data packet][blockentity] and [entity data serializers][entityserializer] use these trusted variants.
`TRUSTED_TAG` and `TRUSTED_COMPOUND_TAG` are variants of `TAG` and `COMPOUND_TAG`, respectively, that have an unlimited heap to decode the tag to, compared to the 2MiB limit of `TAG` and `COMPOUND_TAG`. Trusted tag stream condecs should ideally only be used in clientbound packets, such as what Vanilla does for [block entity data packet][blockentity] and [entity data serializers][entityserializer].
ChampionAsh5357 marked this conversation as resolved.
Show resolved Hide resolved

If a different limit should be used, then a `NbtAccounter` can be supplied with the given size using `ByteBufCodecs#tagCodec` or `#compoundTagCodec`.

Expand Down Expand Up @@ -172,20 +176,22 @@ public static final StreamCodec<RegistryFriendlyByteBuf, Integer> STREAM_CODEC =

A stream codec which supplies an in-code value and encodes to nothing can be represented using `StreamCodec#unit`. This is useful if no information should be synced across the network.
ChampionAsh5357 marked this conversation as resolved.
Show resolved Hide resolved

:::warning
Unit stream codecs expects that any encoded object must match the unit specified; otherwise an error will be thrown. Therefore, all objects must have some `equals` implementation that returns true for the unit object.
ChampionAsh5357 marked this conversation as resolved.
Show resolved Hide resolved
:::

```java
public static final StreamCodec<ByteBuf, IEventBus> UNIT_STREAM_CODEC =
StreamCodec.unit(
() -> NeoForge.EVENT_BUS // Can also be a raw value
);
public static final StreamCodec<ByteBuf, Item> UNIT_STREAM_CODEC =
StreamCodec.unit(Items.AIR);
```
### Lazy Initialized

Sometimes, a stream codec may rely on data that is not present when it is constructed. In these situations `NeoForgeStreamCodecs#lazy` can be used to for a stream codec to construct itself on first read/write. The method takes in a supplied stream codec.
Sometimes, a stream codec may rely on data that is not present when it is constructed. In these situations `NeoForgeStreamCodecs#lazy` can be used for a stream codec to construct itself on first read/write. The method takes in a supplied stream codec.

```java
public static final StreamCodec<ByteBuf, IEventBus> LAZY_STREAM_CODEC =
NeoForgeStreamCodecs.lazy(
() -> StreamCodec.unit(NeoForge.EVENT_BUS)
() -> StreamCodec.unit(Items.AIR)
);
```

Expand Down Expand Up @@ -263,7 +269,7 @@ public enum ExampleIdObject {
ByIdMap.continuous(
ExampleIdObject::getId,
ExampleIdObject.values(),
ByIdMap.OutOfBoundsStrategy.ZERO
ByIdMap.OutOfBoundsStrategy.ZERO
);

ExampleIdObject(int id) { /* ... */ }
Expand Down Expand Up @@ -291,6 +297,10 @@ public static final StreamCodec<RegistryFriendlyByteBuf, Optional<DataComponentT

Registry objects can be sent across the network using one of three methods: `registry`, `holderRegistry`, or `holder`. Each takes in a `ResourceKey` representing the registry the registry object is in.
ChampionAsh5357 marked this conversation as resolved.
Show resolved Hide resolved

:::warning
Custom registries must be syncable by calling `RegistryBuilder#sync` and setting the value to `true`. Otherwise, the encoder will throw an exception.
:::

`registry` and `holderRegistry` returns the registry object or a holder wrapped registry object, respectively. These methods send over an id representing the registry object.

```java
Expand All @@ -312,9 +322,13 @@ public static final StreamCodec<RegistryFriendlyByteBuf, Holder<SoundEvent>> STR
);
```

:::note
`holder` will only throw an exception for a non-synced custom registry if the holder is not direct.
:::

### Holder Sets

Tags or sets of holder wrapped registry objects can be sent using `holderSet`. This takes in a `ResourceKey` represent the registry the registry objects are in.
Tags or sets of holder wrapped registry objects can be sent using `holderSet`. This takes in a `ResourceKey` representing the registry the registry objects are in.

```java
public static final StreamCodec<RegistryFriendlyByteBuf, HolderSet<Item>> HOLDER_SET_STREAM_CODEC =
Expand All @@ -323,7 +337,7 @@ public static final StreamCodec<RegistryFriendlyByteBuf, HolderSet<Item>> HOLDER

### Recursive

Sometimes, an object may reference an object of the same type as a field. For example, `MobEffectinstance` takes in an optional `MobEffectinstance` if there is a hidden effect. In this case, `StreamCodec#recursive` can be used to supply the stream codec as part of a function to create the stream codec.
Sometimes, an object may reference an object of the same type as a field. For example, `MobEffectInstance` takes in an optional `MobEffectInstance` if there is a hidden effect. In this case, `StreamCodec#recursive` can be used to supply the stream codec as part of a function to create the stream codec.

```java
// Define our recursive object
Expand Down
Loading