Skip to content

Effects api 2 #142

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

Open
wants to merge 52 commits into
base: develop
Choose a base branch
from

Conversation

paulevsGitch
Copy link
Contributor

A replacement for PR #117 since the old branch was damaged by multiple changes.

mineLdiver and others added 22 commits May 2, 2024 18:11
Comments are broke, I'll fix that later
…pi-2

# Conflicts:
#	build.gradle.kts
#	src/test/java/net/modificationstation/sltest/item/ItemListener.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationHoeItem.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationShearsItem.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationSwordItem.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolItem.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/StationToolMaterial.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/api/item/tool/ToolLevel.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/impl/item/ToolEffectivenessImplV1.java
#	station-tools-api-v1/src/main/java/net/modificationstation/stationapi/mixin/tools/ToolMaterialMixin.java
#	station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/item/tool/VanillaToolFixImpl.java
@paulevsGitch paulevsGitch mentioned this pull request Jan 2, 2025
@paulevsGitch paulevsGitch marked this pull request as ready for review January 3, 2025 04:48
@paulevsGitch
Copy link
Contributor Author

I redesigned API a bit, now instead of using fixed time effects you can specify the time when adding effect to entity, similar to modern (probably)

Copy link
Member

@calmilamsy calmilamsy left a comment

Choose a reason for hiding this comment

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

Otherwise, I'm happy to see this merged.


public class EffectsRegisterListener {
@EventListener(phase = InitEvent.POST_INIT_PHASE)
public void onInit(InitEvent event) {
Copy link
Member

Choose a reason for hiding this comment

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

Might cause some load order issues because InitEvent is called very early on, not sure though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What kind of issues?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not exactly sure since this is a rather open situation, but there's this piece of documentation from the deprecated PostInitEvent that was replaced with POST_INIT_PHASE:

Minecraft classes can not be referenced during this event.

The reason is that InitEvent is posted on Fabric's PreLaunchEntrypoint, so, before the game is actually started by Fabric Loader.
This could lead to potential very hard to debug issues if an effect class references some parts of Minecraft in its static initializer, causing Minecraft classes to load prematurely.
I'm not completely sure what'd be a good alternative to InitEvent though. Maybe Fabric's own ModInitializer? This one is initialized after most of the game's setup has been done.

Copy link
Member

Choose a reason for hiding this comment

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

Might be an idea to add a "stapi init last step" event.

Copy link
Member

Choose a reason for hiding this comment

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

Literally just before initializationfinished.

Copy link
Member

Choose a reason for hiding this comment

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

No point in such an event really when ModInitializer is already a thing,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if an effect class references some parts of Minecraft in its static initializer

Static initialiser will be not called until at least one method of target class is called, and lambda is not that method, so any static block will be created only when the first instance of effect will appear. You can easily check that by adding static block to any effect like this:

static {
	System.out.println("\n\nEFFECT\n\n");
}

And until at least one effect instance is created that will be zero:

image

So I don't see any problems here at all

Copy link
Member

Choose a reason for hiding this comment

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

The event listener could also be poorly coded, and it's not really obvious that you shouldn't reference a Minecraft class from an event listener that registers an in-game object. Moving it to a later point in the init sequence is simple and foolproof enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not different from other registry events, you also should not call most of game classes in them since otherwise you will just break the game.

The event listener could also be poorly coded

This is a problem of specific listener implementation, not an API. If I will call Minecraft client on server that will be my fault that I did that, not an API that allowed me to do that.

I guess that the simplest solution is just directly specify in Javadocs that game classes should be not called in this event to prevent issues

Copy link
Member

Choose a reason for hiding this comment

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

This still doesn't address the problem of game content (entity effects) being registered before the game even starts initializing. It likely won't cause actual crashes, but can cause confusion.

@mineLdiver
Copy link
Member

I think I'll take over from here.

A few things I want to do:

  1. Change fastutil's Pair to DFU's Pair.
  2. Make the effects registry synchronized and use effect raw IDs in the packets.
  3. Change tabs to spaces.
  4. Change InitEvent to ModInitializer.
  5. Make StAPI's internal listeners private.

If you have no objections to this, I'll start working.

I'd also like to discuss potentially introducing EntityEffectType, similar to PacketType. The problem is that EntityEffectFactory isn't really extendable since it's just an interface, so introducing EntityEffectType would future-proof the API design and make more sense for the registry entry type. It might not be required right now, but it's not a bad thing to have.
I can implement it myself if you're not against it. And, again, this is up for discussion, you're the author of the pull request after all.

# Conflicts:
#	station-registry-api-v0/src/main/java/net/modificationstation/stationapi/api/registry/sync/trackers/Int2ObjectMapTracker.java
@mineLdiver
Copy link
Member

Found a bug - StationEffectEntity#removeAllEffects doesn't invoke EntityEffect#onRemoved.

Gonna fix it.

@mineLdiver
Copy link
Member

Forgot that fastutil also has pairs for primitives. Using that instead of DFU's pair.

@mineLdiver
Copy link
Member

I think I've figured out a typesafe way to have generic factories while not being too complicated for simple effects.

public class TestPlayerEffect extends EntityEffect<PlayerEntity, TestPlayerEffect> {
    public static final EntityEffect.Type<PlayerEntity, TestPlayerEffect> TYPE = Type
            .builder(TestPlayerEffect::new, entity -> entity instanceof PlayerEntity player ? player : null)
            .build();

    private int originalHealth;
    
    protected TestPlayerEffect(PlayerEntity entity, int ticks) {
        super(entity, ticks);
    }
    
    // ...

The instanceof check lambda is optional, there's another builder method which only takes the factory, in which case all generic types default to basic Entity and don't allow factories which take a subclass of Entity.

In the type class there will be a tryCreate method like this:

public Optional<EFFECT> tryCreate(Entity entity, int ticks) {
    return Optional
            .ofNullable(entityFilter.apply(entity))
            .map(filteredEntity -> factory.create(filteredEntity, ticks));
}

entityFilter being the optional instanceof check (or Function#identity if the former isn't provided).

Overall, this should make for an easy to use, typesafe, reflectionless, and foolproof API. Thoughts?

mineLdiver and others added 3 commits July 12, 2025 13:08
# Conflicts:
#	src/test/java/net/modificationstation/sltest/block/Blocks.java
#	station-registry-api-v0/src/main/java/net/modificationstation/stationapi/api/registry/sync/trackers/Int2ObjectMapTracker.java
# Conflicts:
#	buildSrc/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java
#	settings.gradle.kts
@mineLdiver
Copy link
Member

Three bugs:

  1. Metadata doesn't synchronize between server and client. Rejoining a server with an effect causes the client-side metadata to revert to default values, causing undefined behavior, for example TestPlayerEffect setting player's health to 0 client-side on getting removed, showing the respawn screen, while the server sets it back to the original health from the saved NBT value, putting the player in a glitched state.
  2. SendAllEffectsPacket is never sent.
  3. SendAllEffectsPacket is incorrectly marked as serverBound, though expecting client-side on apply and being sent from the server.

Working on it.

@mineLdiver
Copy link
Member

Correction: SendAllEffectsPacket isn't never sent, rather it's only sent to other players when you join with an effect, or to you when you join a server with a player that has an effect. Which then causes bad packet ID on the receiving clients because of the wrong marking.

@mineLdiver
Copy link
Member

Nevermind above correction, it's supposed to work like that, I didn't fully understand its purpose by that time.
However, this unveiled a different issue - all packets are incorrectly marked as serverBound while expecting client on apply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants