Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Commit ea0ad1b

Browse files
authored
Feature/armor (#171)
* Impl IForgeItem.getArmorTexture * Impl IForgeItem.onArmorTick * Fix getArmorModel not being called properly * Apply suggestions
1 parent d1ad8e1 commit ea0ad1b

File tree

8 files changed

+348
-0
lines changed

8 files changed

+348
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.impl.extensions.item;
21+
22+
import javax.annotation.Nullable;
23+
24+
import net.minecraftforge.common.extensions.IForgeItem;
25+
26+
import net.minecraft.client.render.entity.model.BipedEntityModel;
27+
import net.minecraft.entity.Entity;
28+
import net.minecraft.entity.EquipmentSlot;
29+
import net.minecraft.entity.LivingEntity;
30+
import net.minecraft.entity.player.PlayerEntity;
31+
import net.minecraft.item.ItemStack;
32+
import net.minecraft.util.Identifier;
33+
import net.minecraft.world.World;
34+
35+
public interface PatchworkArmorItemHandler {
36+
@SuppressWarnings("rawtypes")
37+
BipedEntityModel getArmorModelHook(LivingEntity entity, ItemStack itemStack, EquipmentSlot slot, BipedEntityModel model);
38+
39+
Identifier getArmorResource(Entity entity, ItemStack stack, EquipmentSlot slot, @Nullable String type);
40+
41+
/**
42+
* Called by mixins(MixinArmorFeatureRenderer) and ForgeHooksClient.
43+
*/
44+
static String patchwork$getArmorTexture(Entity entity, ItemStack itemStack, String defaultTexture, EquipmentSlot slot, String type) {
45+
IForgeItem forgeItem = (IForgeItem) itemStack.getItem();
46+
String result = forgeItem.getArmorTexture(itemStack, entity, slot, type);
47+
return result != null ? result : defaultTexture;
48+
}
49+
50+
static <A extends BipedEntityModel<?>> A patchwork$getArmorModel(LivingEntity entityLiving, ItemStack itemStack, EquipmentSlot slot, A _default) {
51+
IForgeItem forgeItem = (IForgeItem) itemStack.getItem();
52+
A model = forgeItem.getArmorModel(entityLiving, itemStack, slot, _default);
53+
return model == null ? _default : model;
54+
}
55+
56+
/**
57+
* Called by mixins(MixinPlayerInventory) and IForgeItemStack.
58+
*/
59+
static void patchwork$fireArmorTick(ItemStack itemStack, World world, PlayerEntity player) {
60+
IForgeItem item = (IForgeItem) itemStack.getItem();
61+
item.onArmorTick(itemStack, world, player);
62+
}
63+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.mixin.extensions.item;
21+
22+
import org.spongepowered.asm.mixin.Mixin;
23+
import org.spongepowered.asm.mixin.injection.At;
24+
import org.spongepowered.asm.mixin.injection.Inject;
25+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
26+
27+
import net.minecraft.entity.player.PlayerEntity;
28+
import net.minecraft.entity.player.PlayerInventory;
29+
import net.minecraft.world.World;
30+
31+
import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler;
32+
33+
@Mixin(PlayerInventory.class)
34+
public abstract class MixinPlayerInventory {
35+
@Inject(method = "updateItems", at = @At("RETURN"))
36+
private void fireArmorTick(CallbackInfo ci) {
37+
final PlayerInventory me = (PlayerInventory) (Object) this;
38+
final PlayerEntity player = me.player;
39+
final World world = player.world;
40+
me.armor.forEach(itemStack -> PatchworkArmorItemHandler.patchwork$fireArmorTick(itemStack, world, player));
41+
}
42+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.mixin.extensions.item.client;
21+
22+
import org.spongepowered.asm.mixin.Mixin;
23+
24+
import net.minecraft.client.render.entity.feature.ArmorBipedFeatureRenderer;
25+
import net.minecraft.client.render.entity.model.BipedEntityModel;
26+
import net.minecraft.entity.EquipmentSlot;
27+
import net.minecraft.entity.LivingEntity;
28+
import net.minecraft.item.ItemStack;
29+
30+
import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler;
31+
32+
@Mixin(ArmorBipedFeatureRenderer.class)
33+
public abstract class MixinArmorBipedFeatureRenderer implements PatchworkArmorItemHandler {
34+
@SuppressWarnings("rawtypes")
35+
@Override
36+
public BipedEntityModel getArmorModelHook(LivingEntity entity, ItemStack itemStack, EquipmentSlot slot, BipedEntityModel defaultModel) {
37+
return PatchworkArmorItemHandler.patchwork$getArmorModel(entity, itemStack, slot, defaultModel);
38+
}
39+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package net.patchworkmc.mixin.extensions.item.client;
21+
22+
import java.util.Map;
23+
24+
import javax.annotation.Nullable;
25+
26+
import org.objectweb.asm.Opcodes;
27+
import org.spongepowered.asm.mixin.Final;
28+
import org.spongepowered.asm.mixin.Mixin;
29+
import org.spongepowered.asm.mixin.Shadow;
30+
import org.spongepowered.asm.mixin.Unique;
31+
import org.spongepowered.asm.mixin.injection.At;
32+
import org.spongepowered.asm.mixin.injection.At.Shift;
33+
import org.spongepowered.asm.mixin.injection.Constant;
34+
import org.spongepowered.asm.mixin.injection.ModifyConstant;
35+
import org.spongepowered.asm.mixin.injection.ModifyVariable;
36+
import org.spongepowered.asm.mixin.injection.Redirect;
37+
import org.spongepowered.asm.mixin.injection.Slice;
38+
39+
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
40+
import net.minecraft.client.render.entity.model.BipedEntityModel;
41+
import net.minecraft.entity.Entity;
42+
import net.minecraft.entity.EquipmentSlot;
43+
import net.minecraft.entity.LivingEntity;
44+
import net.minecraft.item.ArmorItem;
45+
import net.minecraft.item.DyeableArmorItem;
46+
import net.minecraft.item.DyeableItem;
47+
import net.minecraft.item.ItemStack;
48+
import net.minecraft.item.Items;
49+
import net.minecraft.util.Identifier;
50+
51+
import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler;
52+
53+
/**
54+
* TODO: Check if any Fabric mods calls getArmorTexture and method_4174 directly,
55+
* I don't think so because both are private.
56+
*/
57+
@Mixin(ArmorFeatureRenderer.class)
58+
public abstract class MixinArmorFeatureRenderer implements PatchworkArmorItemHandler {
59+
@Shadow
60+
@Final
61+
private static Map<String, Identifier> ARMOR_TEXTURE_CACHE;
62+
63+
@Shadow
64+
private boolean isLegs(EquipmentSlot equipmentSlot) {
65+
return false;
66+
}
67+
68+
@SuppressWarnings("rawtypes")
69+
@Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0,
70+
target = "net/minecraft/client/render/entity/feature/ArmorFeatureRenderer.getArmor(Lnet/minecraft/entity/EquipmentSlot;)Lnet/minecraft/client/render/entity/model/BipedEntityModel;"))
71+
private BipedEntityModel getArmorModel(ArmorFeatureRenderer me, EquipmentSlot equipmentSlot,
72+
LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot2) {
73+
BipedEntityModel defaultModel = me.getArmor(equipmentSlot);
74+
ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot);
75+
return this.getArmorModelHook(livingEntity, itemStack, equipmentSlot, defaultModel);
76+
}
77+
78+
// In 1.15 and above, getArmorTexture(ArmorItem, boolean) is removed.
79+
@SuppressWarnings("rawtypes")
80+
@Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0,
81+
target = "net/minecraft/client/render/entity/feature/ArmorFeatureRenderer.getArmorTexture(Lnet/minecraft/item/ArmorItem;Z)Lnet/minecraft/util/Identifier;"))
82+
private Identifier getArmorTexture(ArmorFeatureRenderer me, ArmorItem armor, boolean bl,
83+
LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot) {
84+
ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot);
85+
return this.getArmorResource(livingEntity, itemStack, equipmentSlot, null);
86+
}
87+
88+
// In 1.15 and above, method_4174 is renamed to getArmorTexture.
89+
@SuppressWarnings("rawtypes")
90+
@Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0,
91+
target = "net/minecraft/client/render/entity/feature/ArmorFeatureRenderer.method_4174(Lnet/minecraft/item/ArmorItem;ZLjava/lang/String;)Lnet/minecraft/util/Identifier;"))
92+
private Identifier getArmorTexture(ArmorFeatureRenderer me, ArmorItem armor, boolean bl, String overlay,
93+
LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot) {
94+
ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot);
95+
return this.getArmorResource(livingEntity, itemStack, equipmentSlot, overlay);
96+
}
97+
98+
/*
99+
* this.bindTexture(xxxxxxxx); // The first bindTexture() within renderArmor().
100+
* - if (armorItem instanceof DyeableArmorItem) {
101+
* - int m = ((DyeableArmorItem)armorItem).getColor(itemStack);
102+
* + if (armorItem instanceof DyeableItem) {
103+
* + armorItem = hookBeforeTypeCast(armorItem);
104+
* + int m = ((DyeableItem) armorItem).hookGetColor(armorItem, itemStack);
105+
* + armorItem = restoreVar(xxxx);
106+
* float n = (float) (m >> 16 & 255) / 255.0F;
107+
*/
108+
@SuppressWarnings("rawtypes")
109+
@ModifyConstant(method = "renderArmor", constant = @Constant(classValue = DyeableArmorItem.class, ordinal = 0))
110+
private boolean isDyeableItem(Object obj, Class cls) {
111+
return obj instanceof DyeableItem; // Allow this for anything, not only cloth
112+
}
113+
114+
@Unique
115+
private static final String FeatureRenderer_bindTexture = "net/minecraft/client/render/entity/feature/FeatureRenderer.bindTexture(Lnet/minecraft/util/Identifier;)V";
116+
@Unique
117+
private static final String DyeableArmorItem_getColor = "net/minecraft/item/DyeableArmorItem.getColor(Lnet/minecraft/item/ItemStack;)I";
118+
119+
@ModifyVariable(method = "renderArmor", ordinal = 0, at = @At(value = "JUMP", ordinal = 0, opcode = Opcodes.IFEQ, shift = Shift.AFTER),
120+
slice = @Slice(
121+
from = @At(value = "INVOKE", ordinal = 0, target = FeatureRenderer_bindTexture),
122+
to = @At(value = "INVOKE", ordinal = 0, target = DyeableArmorItem_getColor)
123+
))
124+
private ArmorItem hookBeforeTypeCast(ArmorItem armorItem) {
125+
return (DyeableArmorItem) Items.LEATHER_HELMET; // Bypass the checkcast
126+
}
127+
128+
@Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0, target = DyeableArmorItem_getColor))
129+
private int hookGetColor(DyeableArmorItem dummy, ItemStack itemStack) {
130+
return ((DyeableItem) itemStack.getItem()).getColor(itemStack);
131+
}
132+
133+
@ModifyVariable(method = "renderArmor", ordinal = 0, at = @At(value = "INVOKE", ordinal = 0, shift = Shift.AFTER, target = DyeableArmorItem_getColor))
134+
private ArmorItem restoreVar(ArmorItem armorItem,
135+
LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot) {
136+
ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot);
137+
return (ArmorItem) itemStack.getItem();
138+
}
139+
140+
/*=================================== FORGE START =========================================*/
141+
/**
142+
* Hook to allow item-sensitive armor model. for LayerBipedArmor. In Forge, this is protected.
143+
*/
144+
@SuppressWarnings("rawtypes")
145+
@Override
146+
public BipedEntityModel getArmorModelHook(LivingEntity entity, ItemStack itemStack, EquipmentSlot slot, BipedEntityModel model) {
147+
return model;
148+
}
149+
150+
/**
151+
* More generic ForgeHook version of the above function, it allows for Items to
152+
* have more control over what texture they provide.
153+
*
154+
* @param entity Entity wearing the armor
155+
* @param stack ItemStack for the armor
156+
* @param slot Slot ID that the item is in
157+
* @param type Subtype, can be null or "overlay"
158+
* @return ResourceLocation pointing at the armor's texture
159+
*/
160+
@Override
161+
public Identifier getArmorResource(Entity entity, ItemStack stack, EquipmentSlot slot, @Nullable String type) {
162+
ArmorItem item = (ArmorItem) stack.getItem();
163+
String texture = item.getMaterial().getName();
164+
String domain = "minecraft";
165+
int idx = texture.indexOf(':');
166+
167+
if (idx != -1) {
168+
domain = texture.substring(0, idx);
169+
texture = texture.substring(idx + 1);
170+
}
171+
172+
String s1 = String.format("%s:textures/models/armor/%s_layer_%d%s.png", domain, texture, (isLegs(slot) ? 2 : 1),
173+
type == null ? "" : String.format("_%s", type));
174+
175+
s1 = PatchworkArmorItemHandler.patchwork$getArmorTexture(entity, stack, s1, slot, type);
176+
Identifier armorTextureIdentifier = (Identifier) ARMOR_TEXTURE_CACHE.get(s1);
177+
178+
if (armorTextureIdentifier == null) {
179+
armorTextureIdentifier = new Identifier(s1);
180+
ARMOR_TEXTURE_CACHE.put(s1, armorTextureIdentifier);
181+
}
182+
183+
return armorTextureIdentifier;
184+
}
185+
}

patchwork-extensions-item/src/main/resources/patchwork-extensions-item.mixins.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
"MixinEnchantedBookItem",
99
"MixinItem",
1010
"MixinItemSettings",
11+
"MixinPlayerInventory",
1112
"MixinPotionItems",
1213
"MixinSpawnEggItem"
1314
],
1415
"client": [
16+
"client.MixinArmorBipedFeatureRenderer",
17+
"client.MixinArmorFeatureRenderer",
1518
"client.MixinItem",
1619
"client.MixinItemSettings"
1720
],

patchwork-god-classes/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies {
1212
implementation project(path: ':patchwork-events-world', configuration: 'dev')
1313
implementation project(path: ':patchwork-extensions-block', configuration: 'dev')
1414
implementation project(path: ':patchwork-extensions-blockentity', configuration: 'dev')
15+
implementation project(path: ':patchwork-extensions-item', configuration: 'dev')
1516
implementation project(path: ':patchwork-loot', configuration: 'dev')
1617
implementation project(path: ':patchwork-networking', configuration: 'dev')
1718
}

patchwork-god-classes/src/main/java/net/minecraftforge/client/ForgeHooksClient.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,32 @@
2323

2424
import net.minecraft.client.color.block.BlockColors;
2525
import net.minecraft.client.color.item.ItemColors;
26+
import net.minecraft.client.render.entity.model.BipedEntityModel;
2627
import net.minecraft.client.texture.SpriteAtlasTexture;
28+
import net.minecraft.entity.Entity;
29+
import net.minecraft.entity.EquipmentSlot;
30+
import net.minecraft.entity.LivingEntity;
31+
import net.minecraft.item.ItemStack;
2732
import net.minecraft.util.Identifier;
2833
import net.minecraft.client.Mouse;
2934

3035
import net.patchworkmc.impl.event.input.InputEvents;
3136
import net.patchworkmc.impl.event.render.RenderEvents;
37+
import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler;
3238

3339
/*
3440
* Note: this class is intended for mod use only, to dispatch to the implementations kept in their own modules.
3541
* Do not keep implementation details here, methods should be thin wrappers around methods in other modules.
3642
*/
3743
public class ForgeHooksClient {
44+
public static String getArmorTexture(Entity entity, ItemStack armor, String defaultTexture, EquipmentSlot slot, String type) {
45+
return PatchworkArmorItemHandler.patchwork$getArmorTexture(entity, armor, defaultTexture, slot, type);
46+
}
47+
48+
public static <A extends BipedEntityModel<?>> A getArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot slot, A defaultModel) {
49+
return PatchworkArmorItemHandler.patchwork$getArmorModel(livingEntity, itemStack, slot, defaultModel);
50+
}
51+
3852
public static void fireMouseInput(int button, int action, int mods) {
3953
InputEvents.fireMouseInput(button, action, mods);
4054
}

patchwork-god-classes/src/main/resources/fabric.mod.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"patchwork-events-rendering": "*",
2525
"patchwork-events-world": "*",
2626
"patchwork-extensions-block": "*",
27+
"patchwork-extensions-item": "*",
2728
"patchwork-loot": "*"
2829
},
2930
"custom": {

0 commit comments

Comments
 (0)