aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornea <romangraef@gmail.com>2022-02-10 03:06:33 +0100
committernea <romangraef@gmail.com>2022-02-10 03:06:33 +0100
commit3b844cb2c8bcf191a4249114383ea7cc88c8849c (patch)
tree91312d2ee7d92c8503b41f7b5d4a4ced3fa36bb5
parent076733d8ad73a2cfccb910e0bb1dc71c00d8ded1 (diff)
downloadNotEnoughUpdates-3b844cb2c8bcf191a4249114383ea7cc88c8849c.tar.gz
NotEnoughUpdates-3b844cb2c8bcf191a4249114383ea7cc88c8849c.tar.bz2
NotEnoughUpdates-3b844cb2c8bcf191a4249114383ea7cc88c8849c.zip
villager trades
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java1
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java4
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java24
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java21
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java40
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java7
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java9
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java13
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java138
-rw-r--r--src/main/resources/assets/notenoughupdates/dreamskin.pngbin0 -> 220 bytes
-rw-r--r--src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.pngbin0 -> 8633 bytes
-rw-r--r--src/main/resources/mixins.notenoughupdates.json73
12 files changed, 287 insertions, 43 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
index d0b4a7f5..9455eb64 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
@@ -799,6 +799,7 @@ public class APIManager {
if (recipes != null)
RECIPE_ITER:
for (NeuRecipe recipe : recipes) {
+ if (recipe.hasVariableCost() || !recipe.shouldUseForCraftCost()) continue;
float craftPrice = 0;
for (Ingredient i : recipe.getIngredients()) {
if (i.isCoins()) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java
index e820378b..6e4d13f6 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java
@@ -90,7 +90,7 @@ public class GuiItemRecipe extends GuiScreen {
Minecraft.getMinecraft().getTextureManager().bindTexture(currentRecipe.getBackground());
this.drawTexturedModalRect(guiLeft, guiTop, 0, 0, this.xSize, this.ySize);
- currentRecipe.drawExtraBackground(this);
+ currentRecipe.drawExtraBackground(this, mouseX, mouseY);
List<RecipeSlot> slots = getAllRenderedSlots();
for (RecipeSlot slot : slots) {
@@ -101,7 +101,7 @@ public class GuiItemRecipe extends GuiScreen {
Utils.drawStringScaledMaxWidth(title, fontRendererObj, guiLeft + TITLE_X, guiTop + TITLE_Y, false, xSize - 38, 0x404040);
- currentRecipe.drawExtraInfo(this);
+ currentRecipe.drawExtraInfo(this, mouseX, mouseY);
for (RecipeSlot slot : slots) {
if (isWithinRect(mouseX, mouseY, slot.getX(this), slot.getY(this), SLOT_SIZE, SLOT_SIZE)) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java
new file mode 100644
index 00000000..6e8f5cd2
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java
@@ -0,0 +1,24 @@
+package io.github.moulberry.notenoughupdates.mixins;
+
+import net.minecraft.entity.Entity;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(Entity.class)
+public class MixinEntity {
+ // Fix NPE in vanilla code, that we need to work for VillagerTradeRecipe
+ @Inject(method = "getBrightnessForRender", at = @At("HEAD"), cancellable = true)
+ public void onGetBrightnessForRender(float p_getBrightnessForRender_1_, CallbackInfoReturnable<Integer> cir) {
+ if (((Entity) (Object) this).worldObj == null)
+ cir.setReturnValue(-1);
+ }
+
+ // Fix NPE in vanilla code, that we need to work for VillagerTradeRecipe
+ @Inject(method = "getBrightness", at = @At("HEAD"), cancellable = true)
+ public void onGetBrightness(float p_getBrightness_1_, CallbackInfoReturnable<Float> cir) {
+ if (((Entity) (Object) this).worldObj == null)
+ cir.setReturnValue(1.0F);
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java
new file mode 100644
index 00000000..a6f6c0c6
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java
@@ -0,0 +1,21 @@
+package io.github.moulberry.notenoughupdates.mixins;
+
+import net.minecraft.entity.EntityAgeable;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(EntityAgeable.class)
+public class MixinEntityAgeable {
+ @Shadow
+ protected int growingAge;
+
+ // Fix NPE in vanilla code, that we need to work for VillagerTradeRecipe
+ @Inject(method = "getGrowingAge", cancellable = true, at = @At("HEAD"))
+ public void onGetGrowingAge(CallbackInfoReturnable<Integer> cir) {
+ if (((EntityAgeable) (Object) this).worldObj == null)
+ cir.setReturnValue(growingAge);
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java
index 218e03eb..21063ea6 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java
@@ -1,15 +1,26 @@
package io.github.moulberry.notenoughupdates.mixins;
import io.github.moulberry.notenoughupdates.cosmetics.CapeManager;
+import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EnumPlayerModelParts;
+import net.minecraft.scoreboard.ScorePlayerTeam;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraft.util.BlockPos;
+import net.minecraft.world.World;
+import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin({EntityPlayer.class})
public abstract class MixinEntityPlayer {
+ @Shadow
+ public abstract boolean interactWith(Entity par1);
+
@Inject(method = "isWearing", at = @At("HEAD"), cancellable = true)
public void isWearing(EnumPlayerModelParts part, CallbackInfoReturnable<Boolean> cir) {
if (part == EnumPlayerModelParts.CAPE) {
@@ -21,4 +32,33 @@ public abstract class MixinEntityPlayer {
}
}
}
+
+ @Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isRemote:Z", opcode = Opcodes.GETFIELD))
+ public boolean onIsRemote(World instance) {
+ if (instance == null) return true;
+ return instance.isRemote;
+ }
+
+ @Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;getSpawnPoint()Lnet/minecraft/util/BlockPos;"))
+ public BlockPos onGetSpawnPoint(World instance) {
+ if (instance == null)
+ return new BlockPos(0, 0, 0);
+ return instance.getSpawnPoint();
+ }
+
+ @Inject(method = "getWorldScoreboard", at = @At("HEAD"), cancellable = true)
+ public void onGetWorldScoreboard(CallbackInfoReturnable<Scoreboard> cir) {
+ if (((EntityPlayer) (Object) this).worldObj == null) {
+ cir.setReturnValue(null);
+ }
+ }
+
+ @Redirect(method = "getTeam", at = @At(value = "INVOKE", target = "Lnet/minecraft/scoreboard/Scoreboard;getPlayersTeam(Ljava/lang/String;)Lnet/minecraft/scoreboard/ScorePlayerTeam;"))
+ public ScorePlayerTeam onGetTeam(Scoreboard instance, String p_getPlayersTeam_1_) {
+ if (instance == null) {
+ return null;
+ }
+ return instance.getPlayersTeam(p_getPlayersTeam_1_);
+ }
+
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java
index 00e70462..082c1ad8 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java
@@ -45,6 +45,11 @@ public class CraftingRecipe implements NeuRecipe {
}
@Override
+ public boolean hasVariableCost() {
+ return false;
+ }
+
+ @Override
public Set<Ingredient> getOutputs() {
return Collections.singleton(getOutput());
}
@@ -85,7 +90,7 @@ public class CraftingRecipe implements NeuRecipe {
}
@Override
- public void drawExtraInfo(GuiItemRecipe gui) {
+ public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) {
FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
String craftingText = getCraftText();
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java
index 5cbb4afe..2870a54e 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java
@@ -75,6 +75,11 @@ public class ForgeRecipe implements NeuRecipe {
}
@Override
+ public boolean hasVariableCost() {
+ return false;
+ }
+
+ @Override
public Set<Ingredient> getOutputs() {
return Collections.singleton(output);
}
@@ -95,7 +100,7 @@ public class ForgeRecipe implements NeuRecipe {
}
@Override
- public void drawExtraBackground(GuiItemRecipe gui) {
+ public void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) {
Minecraft.getMinecraft().getTextureManager().bindTexture(BACKGROUND);
for (int i = 0; i < inputs.size(); i++) {
int[] slotCoordinates = getSlotCoordinates(i, inputs.size());
@@ -107,7 +112,7 @@ public class ForgeRecipe implements NeuRecipe {
}
@Override
- public void drawExtraInfo(GuiItemRecipe gui) {
+ public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) {
FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
if (timeInSeconds > 0)
Utils.drawStringCenteredScaledMaxWidth(formatDuration(timeInSeconds), fontRenderer, gui.guiLeft + EXTRA_INFO_X, gui.guiTop + EXTRA_INFO_Y, false, EXTRA_INFO_MAX_WIDTH, 0xff00ff);
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java
index cfa091d5..f0363bc8 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java
@@ -15,14 +15,17 @@ public interface NeuRecipe {
List<RecipeSlot> getSlots();
- void drawExtraInfo(GuiItemRecipe gui);
+ default void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) {
+ }
- default void drawExtraBackground(GuiItemRecipe gui) {
+ default void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) {
}
default void drawHoverInformation(GuiItemRecipe gui, int mouseX, int mouseY) {
}
+ boolean hasVariableCost();
+
JsonObject serialize();
ResourceLocation getBackground();
@@ -32,8 +35,14 @@ public interface NeuRecipe {
switch (recipe.get("type").getAsString().intern()) {
case "forge":
return ForgeRecipe.parseForgeRecipe(manager, recipe, output);
+ case "trade":
+ return VillagerTradeRecipe.parseStaticRecipe(manager, recipe);
}
}
return CraftingRecipe.parseCraftingRecipe(manager, recipe, output);
}
+
+ default boolean shouldUseForCraftCost() {
+ return true;
+ }
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java
new file mode 100644
index 00000000..bd3f5be8
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java
@@ -0,0 +1,138 @@
+package io.github.moulberry.notenoughupdates.recipes;
+
+import com.google.common.collect.Sets;
+import com.google.gson.JsonObject;
+import com.mojang.authlib.GameProfile;
+import io.github.moulberry.notenoughupdates.NEUManager;
+import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.AbstractClientPlayer;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.inventory.GuiInventory;
+import net.minecraft.client.network.NetworkPlayerInfo;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.passive.EntityVillager;
+import net.minecraft.util.ResourceLocation;
+
+import java.util.*;
+
+public class VillagerTradeRecipe implements NeuRecipe {
+
+ public static final int COST_SLOT_X = 51;
+ public static final int COST_SLOT_Y = 34;
+ public static final int RESULT_SLOT_Y = 35;
+ public static final int RESULT_SLOT_X = 124;
+
+ private static class Holder { // This holder object exists to defer initialization to first access
+ private static final GameProfile DREAM_PROFILE = new GameProfile(UUID.fromString("ec70bcaf-702f-4bb8-b48d-276fa52a780c"), "Dream");
+ private static final EntityLivingBase DEMO_DREAM = new AbstractClientPlayer(null, DREAM_PROFILE) {
+ @Override
+ protected NetworkPlayerInfo getPlayerInfo() {
+ return new NetworkPlayerInfo(DREAM_PROFILE) {
+ @Override
+ public ResourceLocation getLocationSkin() {
+ return new ResourceLocation("notenoughupdates", "dreamskin.png");
+ }
+ };
+ }
+ };
+ private static final EntityLivingBase DEMO_VILLAGER = new EntityVillager(null);
+
+ private static boolean isAprilFirst() {
+ Calendar cal = Calendar.getInstance();
+ return cal.get(Calendar.DAY_OF_MONTH) == 1 && cal.get(Calendar.MONTH) == Calendar.APRIL;
+ }
+
+ private static final EntityLivingBase DEMO_ENTITY = isAprilFirst() ? DEMO_DREAM : DEMO_VILLAGER;
+
+ }
+
+ private final static ResourceLocation BACKGROUND = new ResourceLocation("notenoughupdates", "textures/gui/villager_recipe.png");
+
+ private final Ingredient result;
+ private final Ingredient cost;
+ private final int minCost, maxCost;
+
+ public VillagerTradeRecipe(Ingredient result, Ingredient cost, int minCost, int maxCost) {
+ this.result = result;
+ this.cost = cost;
+ this.minCost = minCost;
+ this.maxCost = maxCost;
+ }
+
+ public VillagerTradeRecipe(Ingredient result, Ingredient cost) {
+ this(result, cost, -1, -1);
+ }
+
+ public boolean hasVariableCost() {
+ return minCost != -1 && maxCost != -1;
+ }
+
+ @Override
+ public Set<Ingredient> getIngredients() {
+ return Sets.newHashSet(cost);
+ }
+
+ @Override
+ public Set<Ingredient> getOutputs() {
+ return Sets.newHashSet(result);
+ }
+
+ @Override
+ public List<RecipeSlot> getSlots() {
+ return Arrays.asList(
+ new RecipeSlot(COST_SLOT_X, COST_SLOT_Y, cost.getItemStack()),
+ new RecipeSlot(RESULT_SLOT_X, RESULT_SLOT_Y, result.getItemStack())
+ );
+ }
+
+ @Override
+ public boolean shouldUseForCraftCost() {
+ return false;
+ }
+
+ @Override
+ public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) {
+ if (hasVariableCost()) {
+ FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
+ Utils.drawStringCenteredScaledMaxWidth(
+ minCost + " - " + maxCost, fontRenderer,
+ gui.guiLeft + 50, gui.guiTop + 60, false, 75, 0xff00ff);
+
+ }
+ }
+
+ @Override
+ public void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) {
+ GuiInventory.drawEntityOnScreen(gui.guiLeft + 90, gui.guiTop + 75, 30, gui.guiLeft - mouseX + 80, gui.guiTop + 60 - mouseY, Holder.DEMO_ENTITY);
+ }
+
+ @Override
+ public JsonObject serialize() {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("type", "trade");
+ jsonObject.addProperty("result", result.serialize());
+ jsonObject.addProperty("cost", cost.getInternalItemId());
+ if (minCost > 0)
+ jsonObject.addProperty("min", minCost);
+ if (maxCost > 0)
+ jsonObject.addProperty("max", maxCost);
+ return jsonObject;
+ }
+
+ @Override
+ public ResourceLocation getBackground() {
+ return BACKGROUND;
+ }
+
+ public static VillagerTradeRecipe parseStaticRecipe(NEUManager manager, JsonObject recipe) {
+ return new VillagerTradeRecipe(
+ new Ingredient(manager, recipe.get("result").getAsString()),
+ new Ingredient(manager, recipe.get("cost").getAsString()),
+ recipe.has("min") ? recipe.get("min").getAsInt() : -1,
+ recipe.has("max") ? recipe.get("max").getAsInt() : -1
+ );
+ }
+
+}
diff --git a/src/main/resources/assets/notenoughupdates/dreamskin.png b/src/main/resources/assets/notenoughupdates/dreamskin.png
new file mode 100644
index 00000000..067d396e
--- /dev/null
+++ b/src/main/resources/assets/notenoughupdates/dreamskin.png
Binary files differ
diff --git a/src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.png b/src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.png
new file mode 100644
index 00000000..42b6241c
--- /dev/null
+++ b/src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.png
Binary files differ
diff --git a/src/main/resources/mixins.notenoughupdates.json b/src/main/resources/mixins.notenoughupdates.json
index bec344ec..a566ee2e 100644
--- a/src/main/resources/mixins.notenoughupdates.json
+++ b/src/main/resources/mixins.notenoughupdates.json
@@ -3,40 +3,41 @@
"refmap": "mixins.notenoughupdates.refmap.json",
"compatibilityLevel": "JAVA_8",
"mixins": [
- "MixinMinecraft",
- "MixinAbstractClientPlayer",
- "MixinContainer",
- "MixinEffectRenderer",
- "MixinEntityPlayer",
- "MixinEntityPlayerSP",
- "MixinEntityRenderer",
- "MixinGuiChest",
- "MixinGuiContainer",
- "MixinGuiIngame",
- "MixinGuiInventory",
- "MixinGuiScreen",
- "MixinInventoryEffectRenderer",
- "MixinInventoryPlayer",
- "MixinItemCameraTransforms",
- "MixinItemRenderer",
- "MixinItemStack",
- "MixinLayerArmorBase",
- "MixinLayerCustomHead",
-
- "MixinMouseHelper",
- "MixinNetHandlerPlayClient",
- "MixinPlayerControllerMP",
- "MixinRender",
- "MixinRendererLivingEntity",
- "MixinRenderFish",
- "MixinRenderGlobal",
- "MixinRenderItem",
- "MixinRenderList",
- "MixinTextureManager",
- "MixinTileEntitySkullRenderer",
- "MixinTileEntitySpecialRenderer",
- "MixinVboRenderList",
- "MixinWorld",
- "MixinWorldClient"
+ "MixinAbstractClientPlayer",
+ "MixinContainer",
+ "MixinEffectRenderer",
+ "MixinEntity",
+ "MixinEntityAgeable",
+ "MixinEntityPlayer",
+ "MixinEntityPlayerSP",
+ "MixinEntityRenderer",
+ "MixinGuiChest",
+ "MixinGuiContainer",
+ "MixinGuiIngame",
+ "MixinGuiInventory",
+ "MixinGuiScreen",
+ "MixinInventoryEffectRenderer",
+ "MixinInventoryPlayer",
+ "MixinItemCameraTransforms",
+ "MixinItemRenderer",
+ "MixinItemStack",
+ "MixinLayerArmorBase",
+ "MixinLayerCustomHead",
+ "MixinMinecraft",
+ "MixinMouseHelper",
+ "MixinNetHandlerPlayClient",
+ "MixinPlayerControllerMP",
+ "MixinRender",
+ "MixinRendererLivingEntity",
+ "MixinRenderFish",
+ "MixinRenderGlobal",
+ "MixinRenderItem",
+ "MixinRenderList",
+ "MixinTextureManager",
+ "MixinTileEntitySkullRenderer",
+ "MixinTileEntitySpecialRenderer",
+ "MixinVboRenderList",
+ "MixinWorld",
+ "MixinWorldClient"
]
-} \ No newline at end of file
+}