Skip to content

Commit 2591cf9

Browse files
committed
add custom_name conditional for regex & backport 25w03a component select
1 parent f6338a2 commit 2591cf9

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ A vanilla-like implementation of Optifine's CIT, shaped how I believe it would b
1616
- predicate
1717
- fields
1818
- predicate: A item sub predicate [REQUIRED]
19+
- custom_name
20+
- fields
21+
- regex: The regex in string form, to test on the item's custom name
1922

2023
#### Range Dispatch:
2124

@@ -26,6 +29,7 @@ A vanilla-like implementation of Optifine's CIT, shaped how I believe it would b
2629
#### Select:
2730

2831
- rarity (value of the stack rarity component)
32+
- "minecraft:component" (Backported from 25w03a, self-explanatory)
2933

3034
## Examples
3135

@@ -145,4 +149,49 @@ iron_sword.json
145149
}
146150
}
147151
}
152+
```
153+
154+
bone.json
155+
156+
```json
157+
{
158+
"model": {
159+
"type": "minecraft:condition",
160+
"property": "chit:custom_name",
161+
"regex": "lol",
162+
"on_true": {
163+
"type": "minecraft:model",
164+
"model": "minecraft:item/snowball"
165+
},
166+
"on_false": {
167+
"type": "minecraft:model",
168+
"model": "minecraft:item/bone"
169+
}
170+
}
171+
}
172+
```
173+
174+
stick.json
175+
176+
```json
177+
{
178+
"model": {
179+
"type": "minecraft:select",
180+
"property": "minecraft:component",
181+
"component": "minecraft:custom_name",
182+
"cases": [
183+
{
184+
"when": "{\"color\":\"yellow\",\"italic\":false,\"bold\":true,\"text\":\"Hello world\"}",
185+
"model": {
186+
"type": "minecraft:model",
187+
"model": "minecraft:item/blaze_rod"
188+
}
189+
}
190+
],
191+
"fallback": {
192+
"type": "minecraft:model",
193+
"model": "minecraft:item/stick"
194+
}
195+
}
196+
}
148197
```

src/main/kotlin/btw/lowercase/chit/ChitClientMod.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import btw.lowercase.chit.property.conditional.HasEnchantmentConditional
44
import btw.lowercase.chit.property.conditional.HasEnchantmentsConditional
55
import btw.lowercase.chit.property.conditional.PredicateConditional
66
import btw.lowercase.chit.property.numeric.EnchantmentLevelNumeric
7+
import btw.lowercase.chit.property.select.ComponentContents
8+
import btw.lowercase.chit.property.conditional.CustomNameConditional
79
import btw.lowercase.chit.property.select.RaritySelect
810
import net.fabricmc.api.ClientModInitializer
911
import net.minecraft.client.renderer.item.properties.conditional.ConditionalItemModelProperties
1012
import net.minecraft.client.renderer.item.properties.numeric.RangeSelectItemModelProperties
1113
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperties
14+
import net.minecraft.core.component.DataComponents
1215
import net.minecraft.resources.ResourceLocation
1316

1417
object ChitClientMod : ClientModInitializer {
@@ -19,7 +22,23 @@ object ChitClientMod : ClientModInitializer {
1922
RangeSelectItemModelProperties.ID_MAPPER.put(id("enchantment_level"), EnchantmentLevelNumeric.CODEC)
2023

2124
// Components
25+
run {
26+
/**
27+
*
28+
* NOTE:
29+
* I do not own this part of this code, this code has been sourced from 25w03a and
30+
* belongs to Mojang/Minecraft!
31+
*
32+
* I will remove this if any issues arise.
33+
*
34+
*/
35+
SelectItemModelProperties.ID_MAPPER.put(
36+
ResourceLocation.withDefaultNamespace("component"),
37+
ComponentContents.castType<DataComponents>()
38+
)
39+
}
2240
SelectItemModelProperties.ID_MAPPER.put(id("rarity"), RaritySelect.CODEC)
41+
ConditionalItemModelProperties.ID_MAPPER.put(id("custom_name"), CustomNameConditional.CODEC)
2342

2443
// Other
2544
ConditionalItemModelProperties.ID_MAPPER.put(id("predicate"), PredicateConditional.CODEC)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package btw.lowercase.chit.property.conditional
2+
3+
import com.mojang.serialization.MapCodec
4+
import com.mojang.serialization.codecs.RecordCodecBuilder
5+
import net.minecraft.client.multiplayer.ClientLevel
6+
import net.minecraft.client.renderer.item.properties.conditional.ConditionalItemModelProperty
7+
import net.minecraft.util.ExtraCodecs
8+
import net.minecraft.world.entity.LivingEntity
9+
import net.minecraft.world.item.ItemDisplayContext
10+
import net.minecraft.world.item.ItemStack
11+
import kotlin.text.Regex
12+
13+
class CustomNameConditional(val regex: String) : ConditionalItemModelProperty {
14+
companion object {
15+
val CODEC = RecordCodecBuilder.mapCodec { instance ->
16+
instance.group(
17+
ExtraCodecs.NON_EMPTY_STRING.fieldOf("regex").forGetter(CustomNameConditional::regex)
18+
).apply(instance, ::CustomNameConditional)
19+
}
20+
}
21+
22+
override fun get(
23+
stack: ItemStack,
24+
clientLevel: ClientLevel?,
25+
livingEntity: LivingEntity?,
26+
layer: Int,
27+
itemDisplayContext: ItemDisplayContext,
28+
): Boolean {
29+
val regex = Regex.fromLiteral(regex)
30+
return if (stack.customName != null) {
31+
regex.matches(stack.customName!!.string)
32+
} else {
33+
false
34+
}
35+
}
36+
37+
override fun type(): MapCodec<out ConditionalItemModelProperty?> {
38+
return CODEC
39+
}
40+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package btw.lowercase.chit.property.select
2+
3+
import com.google.common.collect.HashMultiset
4+
import com.mojang.serialization.Codec
5+
import com.mojang.serialization.DataResult
6+
import com.mojang.serialization.MapCodec
7+
import net.minecraft.client.multiplayer.ClientLevel
8+
import net.minecraft.client.renderer.item.SelectItemModel.SwitchCase
9+
import net.minecraft.client.renderer.item.SelectItemModel.UnbakedSwitch
10+
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty
11+
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty.Type
12+
import net.minecraft.core.component.DataComponentType
13+
import net.minecraft.core.registries.BuiltInRegistries
14+
import net.minecraft.world.entity.LivingEntity
15+
import net.minecraft.world.item.ItemDisplayContext
16+
import net.minecraft.world.item.ItemStack
17+
18+
/**
19+
*
20+
* NOTE:
21+
* I do not own this part of this code, this code has been sourced from 25w03a and
22+
* belongs to Mojang/Minecraft!
23+
*
24+
* I will remove this if any issues arise.
25+
*
26+
*/
27+
28+
fun <T> validateCases(list: List<SwitchCase<T>>): DataResult<List<SwitchCase<T>>> {
29+
if (list.isEmpty()) {
30+
return DataResult.error {
31+
return@error "Empty case list"
32+
}
33+
} else {
34+
val multiset = HashMultiset.create<T>()
35+
val iterator = list.iterator()
36+
while (iterator.hasNext()) {
37+
val switchCase = iterator.next()
38+
multiset.addAll(switchCase.values())
39+
}
40+
41+
return if (multiset.size != multiset.entrySet().size) {
42+
DataResult.error {
43+
val duplicates = multiset.entrySet()
44+
.filter { it.count > 1 }
45+
.joinToString(", ") { it.element.toString() }
46+
"Duplicate case conditions: $duplicates"
47+
}
48+
} else {
49+
DataResult.success(list)
50+
}
51+
}
52+
}
53+
54+
fun <T> createCasesFieldCodec(codec: Codec<T>): MapCodec<List<SwitchCase<T>>> {
55+
return SwitchCase.codec(codec).listOf().validate(::validateCases).fieldOf("cases")
56+
}
57+
58+
@Suppress("UNCHECKED_CAST")
59+
class ComponentContents<T>(val componentType: DataComponentType<T>) : SelectItemModelProperty<T> {
60+
companion object {
61+
private val TYPE: Type<out ComponentContents<*>, *> = createType<Any>()
62+
63+
private fun <T> createType(): Type<ComponentContents<T>, T> {
64+
val codec: Codec<out DataComponentType<*>> = BuiltInRegistries.DATA_COMPONENT_TYPE.byNameCodec()
65+
.validate { dataComponentType ->
66+
if (dataComponentType.isTransient) {
67+
DataResult.error { "Component can't be serialized" }
68+
} else {
69+
DataResult.success(dataComponentType)
70+
}
71+
}
72+
73+
return Type((codec as Codec<DataComponentType<T>>).dispatchMap("component", { unbakedSwitch ->
74+
(unbakedSwitch.property() as ComponentContents<*>).componentType as DataComponentType<T>?
75+
}, { dataComponentType ->
76+
createCasesFieldCodec(dataComponentType.codecOrThrow())
77+
.xmap(
78+
{ list -> UnbakedSwitch(ComponentContents(dataComponentType), list) },
79+
{ UnbakedSwitch<*, *>::cases as List<SwitchCase<T>> }
80+
)
81+
}))
82+
}
83+
84+
fun <T> castType(): Type<ComponentContents<T>, T> {
85+
return TYPE as Type<ComponentContents<T>, T>
86+
}
87+
}
88+
89+
override fun get(
90+
stack: ItemStack,
91+
clientLevel: ClientLevel?,
92+
livingEntity: LivingEntity?,
93+
layer: Int,
94+
itemDisplayContext: ItemDisplayContext,
95+
): T? {
96+
return stack.get(componentType)
97+
}
98+
99+
override fun type(): Type<ComponentContents<T>, T> {
100+
return castType()
101+
}
102+
}

0 commit comments

Comments
 (0)