
Add data-driven shields
StellarWind22 opened this issue ยท 8 comments
Allow items to be treated as shields by simply including them in the #fabricshieldlib:shields
tag. Any shields registered this way will just use default settings(cooldown);
Still brainstorming it. Currently we're more focused on updating the wiki page and ironing out bugs.
Another way of doing this is to take advantage of the DataComponent system introduced in 1.21.
We will consider all items in #fabricshieldlib:shields
as shields (perhaps should be the conventional shield tag?). When a player is holding these items, he can block attacks just like a vanilla shield. The cooling downtime and enchantment value are the same as the vanilla shield.
If a modded shield wants to have a different cooling downtime or enchantment value, the modder can add a newly-defined
DataComponentType called FabricShieldProperty and put specifications there. If FabricShieldProperty is not present on a modded shield's DataComponent list, vanilla values will be used.
The int getCoolDownTicks();
and boolean supportsBanner();
in interface FabricShield
should return the vanilla value by default, so that modders can simply implement the FabricShield
interface to make their custom shield.
Speaking of the data-driven shields, my suggestion is to create a shield registry similar to how the vanilla biome registry works. The shield characteristics are described in JSON files (/data/<mod_id>/fabricshieldlib/shield/<shield_name>.json
). The "shield_name" should match the item name of the shield. Each of those JSON files will be serialized into instances of a Java record (Lets call it FabricShieldProperty).
We use Mixin to add a method (public @Nullable FabricShieldProperties getShieldProperties();
) to the vanilla Item class. Upon the first call, it looks up the FabricShieldProperty from the shield registry and then caches the result (can be null). In the Fabric shield lib implementation where we need to get info about the shield, we first retrieve the item, then examine the return of its getShieldProperties()
method. A shield should have a non-null return value.
The
int getCoolDownTicks();
andboolean supportsBanner();
in interfaceFabricShield
should return the vanilla value by default, so that modders can simply implement theFabricShield
interface to make their custom shield.
Yeah, makes sense! I wonder why we ddint do that in the first place lol.
We will consider all items in
#fabricshieldlib:shields
as shields (perhaps should be the conventional shield tag?).
We only use the proprietary tag in versions where the c: isnt available.
When a player is holding these items, he can block attacks just like a vanilla shield. The cooling downtime and enchantment value are the same as the vanilla shield.
Yeah that's basically @StellarWind22 's initial plan with this. Would this just entail changing every check for "FabricShield" into a check for "c:shield"?
If a modded shield wants to have a different cooling downtime or enchantment value, the modder can add a newly-defined DataComponentType called FabricShieldProperty and put specifications there.
Yeah, sounds good!
newly-defined DataComponentType called FabricShieldProperty and put specifications there. If FabricShieldProperty is not present on a modded shield's DataComponent list, vanilla values will be used.
Sorry, but I am not very familiar with DataCompnents yet (school hasn't given me time to look into them), but how would this change the item initialization process? Would devs still just do new fabricshielditem or would we have to change the process up?
See my progress here: https://github.com/rikka0w0/Fabric-Shield-Lib/tree/experimental
Will check out after exams along with your PR #182!
See my progress here: https://github.com/rikka0w0/Fabric-Shield-Lib/tree/experimental
See my progress here: https://github.com/rikka0w0/Fabric-Shield-Lib/tree/experimental
I LOVE the FabricShieldUtils.java you made! I still have to look at the rest of the repo, but I just wanted to check out the utils file after I saw it in your PR/
Yeah that's basically @StellarWind22 's initial plan with this. Would this just entail changing every check for "FabricShield" into a check for "c:shield"?
Exactly! The check will be:
public static boolean isShieldItem(Item item) {
return item.getRegistryEntry().isIn(ConventionalItemTags.SHIELD_TOOLS);
}
Would devs still just do new fabricshielditem or would we have to change the process up?
The process will be very different but align better with the vanilla style.
The FabricShield interface could be gone, and customization will be done with DataComponents. Keep FabricShield if we want to have itemstack-sensitive version of getCooldownTicks
. Having FabricShield interface also make porting to older Minecraft a lot easier.
Moreover, modded shields won't need to be an instance of the FabricShieldItem or the vanilla ShieldItem. It only needs to extend the vanilla Item class and the shield check will be done with the "c:shield" tag.
FabricShieldItem will be reserved for demo or internal testing only.
If a developer just wants to implement a shield with vanilla behavior (vanilla cooldown ticks, no banner support), he simply needs to create a normal Item and add it to the "c:shield" tag.
If the developer wants finer control or to alter the shield behavior, he can attach these DataComponents defined in FabricShieldDataComponents
:
public static final ComponentType<Integer> COOLDOWN_TICKS = register("shield_cooldown_ticks",
builder -> builder.codec(Codecs.POSITIVE_INT).packetCodec(PacketCodecs.VAR_INT));
public static final ComponentType<Unit> SHOW_COOLDOWN_TICKS = register("shield_show_cooldown_ticks",
builder -> builder.codec(Codec.unit(Unit.INSTANCE)).packetCodec(PacketCodec.unit(Unit.INSTANCE)));
public static final ComponentType<Unit> SUPPORT_BANNER = register("shield_support_banner",
builder -> builder.codec(Codec.unit(Unit.INSTANCE)).packetCodec(PacketCodec.unit(Unit.INSTANCE)));
These data components are optional. If a shield doesn't have any of the above data components, the corresponding vanilla value will be used.
In addition, I would say, it will be a good idea to put things like FabricShieldDataComponents
and FabricShieldUtils
that developers are allowed to call in a separate namespace, namely api
. In this way, we can maintain a well-defined interface and reduce the chance of creating incompatibility. Developers are not supposed to call anything except for whats in api
.
If a Minecraft version does not support tag (1.12.2 and before), we use the FabricShield interface to check.
We could also use the registry system to provide shield data.