-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
1.21.x Capability Factory System PR #10118
base: 1.21.x
Are you sure you want to change the base?
1.21.x Capability Factory System PR #10118
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work so far. Before I move on with proper testing, please note the comments I've left, especially the ones in IForgeResourceKey
.
default Holder<T> getOrThrow(BlockEntity blockEntity) { | ||
return getOrThrow(blockEntity.getLevel()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this method is a good idea. You should really be directly calling getOrThrow(Level)
if you want to do it like this.
default Holder<T> getOrThrow(Entity entity) { | ||
return getOrThrow(entity.registryAccess()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for this. The entity's registry access method is simply calling its level reference's. Just using the single getOrThrow(Level)
method is probably for the best, so as to not cause confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
He and I talked about this, these are meant to be helper functions that yes are strictly not necessary but people have complained that they want easier access.
default Holder<T> getOrThrow(Level level) { | ||
return getOrThrow(level.registryAccess()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add documentation/comment explanation to this method, similar to getOrThrow(RegistryAccess)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing license header. See existing files in project for examples.
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing license header. See existing files in project for examples.
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing license header. See existing files in project for examples.
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test should clearly indicate the changes that this PR brings. So, I'd like for you to note with comments where the capability factory system is being used.
@@ -40,6 +40,7 @@ | |||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; | |||
import net.minecraft.world.phys.Vec3; | |||
import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions; | |||
import net.minecraftforge.common.capabilities.CapabilityFactoryManager; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import, please remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interface extensions are not patches, so we don't need to be as careful when implementing them as we do with patches. That being said, you should still watch out for the redundancies I've noted in this file.
|
||
import net.minecraft.resources.ResourceLocation; | ||
|
||
public interface ICapabilityFactory<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should mark this interface with @FunctionalInterface
since these factories are effectively lambdas that can be used with the system individually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more of my personal opinion, but a lot of the code in this file is hard to read. I would appreciate it if you could either add relevant JavaDoc or explanation comments to some of the methods in this. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like with CapabilityFactoryHolder
, additional comments would be appreciated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good start, but some comments.
Additionally:
- Needs documentation written in the PR description, ideally a migration guide as well
- Internals marking with
@ApiStatus.Internal
import java.util.Map; | ||
|
||
public final class CapabilityFactoryManager { | ||
private static final CapabilityFactoryManager MANAGER = new CapabilityFactoryManager(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole class can be simplified by making the methods and fields static rather than requiring a level of indirection through a singleton
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya this INSTANCE field pattern is a thing from cpw's days its better to not use this indirect.
On top of that, this class needs to be annotated as internal, as the only thing needing to be public is the init function, which could potentially just be an event handler. Will need to look into that more. Point is this isn't mean to be modder facing in any way.
return MANAGER; | ||
} | ||
|
||
private final Map<Class<?>, Map<ResourceLocation, ICapabilityFactory<?, ?>>> factory = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maps with keys of type Class<?> should use IdentityHashMap for better performance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you didn't mind, can you explain why that is so I know for future reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- HashMap uses
hashCode()
andequals()
. - IdentityHashMap uses
identityHashCode()
and==
.
By explicitly operating on reference identity rather than object equality, the internal algorithms used for the map can be tailored to take advantage of that.
In my testing with the two map types for Class<?> keys in my own EventBus, the IdentityHashMap was consistently faster than a plain HashMap.
import java.util.Map; | ||
|
||
public class CapabilityFactoryRegisterEvent extends Event { | ||
final Map<Class<?>, Map<ResourceLocation, ICapabilityFactory<?, ?>>> factory = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IdentityHashMap here as well
|
||
public class CapabilityProviderWithFactory<B extends ICapabilityProviderImpl<B>> implements ICapabilityProviderImpl<B> { | ||
@VisibleForTesting | ||
static boolean SUPPORTS_LAZY_CAPABILITIES = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fields that are not static final should not be SCREAMING_SNAKE_CASE. Either add final here or change to camelCase
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was copied from CapabilityProvider
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your point being?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep existing behavior?
src/main/java/net/minecraftforge/common/capabilities/CapabilityProviderWithFactory.java
Outdated
Show resolved
Hide resolved
|
||
public CapabilityFactoryRegisterEvent() {} | ||
|
||
public <G, H extends ICapabilityProvider> void register(Class<G> gClass, ResourceLocation resourceLocation, ICapabilityFactory<G, H> factory) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I get a better explanation/docs for the differences between these two functions?
I am not quite understanding why you have CapabilityProviderHolder as public API.
Your example code has noop listeners
and singleton providers
I'll look into it a little more but we can talk about this more on discord.
@RealMangorage, this pull request has conflicts, please resolve them for this PR to move forward. |
The goal of this PR is to provide a way for devs to register Capability Providers Factories which handle creating their providers, vs having em create it inside of an event every tick.