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

Data Attachment Sync API #4049

Merged
merged 43 commits into from
Nov 12, 2024
Merged

Conversation

Syst3ms
Copy link
Contributor

@Syst3ms Syst3ms commented Aug 22, 2024

Completes the data attachment API with client-server syncing capabilities.

Motivation

The existing API works great for attaching data to game objects, be they serverside or clientside, but lacks any ability to synchronize between the two.

A mod that wants to add a "thirst" mechanic can easily do so on the server side by attaching an integer to every player. However, the mod may also want to use this information to render additional HUD elements on the client. Currently, informing the client of this data can only be done manually, which is cumbersome, error-prone, and is much better-suited as an API feature.

API Changes

The API doesn't change a lot (at least compared to the implementation), with a few new methods and one new class.

One new method has been added to AttachmentRegistry.Builder, namely syncWith. It declares that an attachment type may be synchronized with some clients, and takes a PacketCodec to encode attachment data over the network, as well as an element of the new AttachmentSyncPredicate interface.

This interface extends BiPredicate<AttachmentTarget, ServerPlayerEntity> to allow for user-defined predicates, and provides some common presets:

  • all(): attachment data will be synchronized with all clients (that track the target).
  • targetOnly(): attachment data will only be synchronized with the target it is attached to, when it is a player. If the target is not a player, it won't be synchronized with any client.
  • allButTarget(): reverse of the above. For non-player targets, attachment data will be synchronized with all clients.

NOTE: for a user-defined condition, whether attachment data is synchronized with a client can change at runtime (e.g. "sync only with operators" when a player changes operator status). A specialized method to "refresh" data was considered, but in the end discarded due to complexity. It falls to individual mods to handle these edge cases.

AttachmentType also gets one new isSynced method to know whether a given attachment type can be synced.

Usage

Here is how one could register an attachment for a "thirst" meter like mentioned above.

public static final AttachmentType<Integer> THIRST = AttachmentRegistry.<Integer>builder() 
    .initializer(() -> 20) // start with a default value like hunger
    .persistent(Codec.INT) // persist across restarts
    .syncWith(PacketCodecs.VAR_INT.cast(), AttachmentSyncPredicate.targetOnly()) // only the player's own client needs the value for rendering
    .buildAndRegister(Identifier.of("modid", "thirst"));

To-do:

  • Add API options for "sync with all players", "sync with target only", "sync with non-targets only"
  • Use the above to precompute "global" changes that can be broadcast to anyone on the fly
  • Finish testmod
  • Test testmod

Defines 3 payload/packet types based on targets: Chunks + block entities, Entities, Worlds.

Each of these require being sent at out-of-sync times, and so need to be handled separately.
* Added more payload types, renamed a bunch
* Wrote logic for sending each type of packet, client and serverside
* Fixed interface injection (pointed to ServerWorld instead of World)
* Began work on testmod

TODO: a lot of the syncing code is very unoptimized
* Added API for common ways of syncing
* Optimized the packet creation code by caching and bookkeeping ahead of time
Copy link
Member

@modmuss50 modmuss50 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a good start, however there is a lot going on here. We need to be especially careful with C2S packets. Im especially worried about the refreshAttachments, in an ideal world I dont think it should be needed at all.

* write AttachmentChange data to bytes before sending, and use that to check
* reorganize folders
* unify the builder methods by introducing AttachmentSyncPredicate
* Now stored in the ClientConnection
* Will log a warning on configuration if there are syncable server attachments that aren't supported on a client
* Fixed forgetting to store packet codec
@Syst3ms Syst3ms marked this pull request as ready for review September 4, 2024 09:37
@Syst3ms Syst3ms requested review from a team September 4, 2024 09:37
Copy link
Contributor Author

@Syst3ms Syst3ms left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some code changes are still required, before trying the testmod

@Syst3ms
Copy link
Contributor Author

Syst3ms commented Oct 20, 2024

At long last, I found it within me to properly test the testmod on a dedicated server, and squashed all relevant issues! In any case, I would recommend #4109 be merged before this one.

@modmuss50
Copy link
Member

This is looking good, I havent done a full review but nothing stands out to me from a quick look so I dont expect many changes. I have re-ran the server test.

# Conflicts:
#	fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentRegistryImpl.java
@modmuss50 modmuss50 added last call If you care, make yourself heard right away! enhancement New feature or request labels Nov 3, 2024
@modmuss50
Copy link
Member

Added to last call, sorry it took me so long (Factorio is partly to blame).

The API looks solid, we could likely go back and forth on the impl details for a year so I think its best to get something out and if there are problems fix them as and when.

DaRealTurtyWurty added a commit to DaRealTurtyWurty/Industria that referenced this pull request Nov 11, 2024
@modmuss50 modmuss50 added the merge me please Pull requests that are ready to merge label Nov 12, 2024
@modmuss50 modmuss50 merged commit 8e331c5 into FabricMC:1.21.1 Nov 12, 2024
4 checks passed
modmuss50 pushed a commit that referenced this pull request Nov 12, 2024
Completes the data attachment API with client-server syncing capabilities.

## Motivation

The existing API works great for attaching data to game objects, be they serverside or clientside, but lacks any ability to synchronize between the two.

A mod that wants to add a "thirst" mechanic can easily do so on the server side by attaching an integer to every player. However, the mod may also want to use this information to render additional HUD elements on the client. Currently, informing the client of this data can only be done manually, which is cumbersome, error-prone, and is much better-suited as an API feature.

## API Changes

The API doesn't change a lot (at least compared to the implementation), with a few new methods and one new class.

One new method has been added to `AttachmentRegistry.Builder`, namely `syncWith`. It declares that an attachment type may be synchronized with some clients, and takes a `PacketCodec` to encode attachment data over the network, as well as an element of the new `AttachmentSyncPredicate` interface.

This interface extends `BiPredicate<AttachmentTarget, ServerPlayerEntity>` to allow for user-defined predicates, and provides some common presets:
* `all()`: attachment data will be synchronized with all clients (that track the target).
* `targetOnly()`: attachment data will only be synchronized with the target it is attached to, when it is a player. If the target is not a player, it won't be synchronized with any client.
* `allButTarget()`: reverse of the above. For non-player targets, attachment data will be synchronized with all clients.

**NOTE**: for a user-defined condition, whether attachment data is synchronized with a client can change at runtime (e.g. "sync only with operators" when a player changes operator status). A specialized method to "refresh" data was considered, but in the end discarded due to complexity. It falls to individual mods to handle these edge cases.

AttachmentType also gets one new `isSynced` method to know whether a given attachment type can be synced.

## Usage

Here is how one could register an attachment for a "thirst" meter like mentioned above.
```java
public static final AttachmentType<Integer> THIRST = AttachmentRegistry.<Integer>builder()
    .initializer(() -> 20) // start with a default value like hunger
    .persistent(Codec.INT) // persist across restarts
    .syncWith(PacketCodecs.VAR_INT.cast(), AttachmentSyncPredicate.targetOnly()) // only the player's own client needs the value for rendering
    .buildAndRegister(Identifier.of("modid", "thirst"));
```
modmuss50 pushed a commit that referenced this pull request Nov 12, 2024
Completes the data attachment API with client-server syncing capabilities.

## Motivation

The existing API works great for attaching data to game objects, be they serverside or clientside, but lacks any ability to synchronize between the two.

A mod that wants to add a "thirst" mechanic can easily do so on the server side by attaching an integer to every player. However, the mod may also want to use this information to render additional HUD elements on the client. Currently, informing the client of this data can only be done manually, which is cumbersome, error-prone, and is much better-suited as an API feature.

## API Changes

The API doesn't change a lot (at least compared to the implementation), with a few new methods and one new class.

One new method has been added to `AttachmentRegistry.Builder`, namely `syncWith`. It declares that an attachment type may be synchronized with some clients, and takes a `PacketCodec` to encode attachment data over the network, as well as an element of the new `AttachmentSyncPredicate` interface.

This interface extends `BiPredicate<AttachmentTarget, ServerPlayerEntity>` to allow for user-defined predicates, and provides some common presets:
* `all()`: attachment data will be synchronized with all clients (that track the target).
* `targetOnly()`: attachment data will only be synchronized with the target it is attached to, when it is a player. If the target is not a player, it won't be synchronized with any client.
* `allButTarget()`: reverse of the above. For non-player targets, attachment data will be synchronized with all clients.

**NOTE**: for a user-defined condition, whether attachment data is synchronized with a client can change at runtime (e.g. "sync only with operators" when a player changes operator status). A specialized method to "refresh" data was considered, but in the end discarded due to complexity. It falls to individual mods to handle these edge cases.

AttachmentType also gets one new `isSynced` method to know whether a given attachment type can be synced.

## Usage

Here is how one could register an attachment for a "thirst" meter like mentioned above.
```java
public static final AttachmentType<Integer> THIRST = AttachmentRegistry.<Integer>builder()
    .initializer(() -> 20) // start with a default value like hunger
    .persistent(Codec.INT) // persist across restarts
    .syncWith(PacketCodecs.VAR_INT.cast(), AttachmentSyncPredicate.targetOnly()) // only the player's own client needs the value for rendering
    .buildAndRegister(Identifier.of("modid", "thirst"));
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request last call If you care, make yourself heard right away! merge me please Pull requests that are ready to merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants