From 3507cb85c5960682ce522dc9a2637ccf77e2fff7 Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 20 Feb 2024 20:29:49 -0500 Subject: [PATCH] Multithread Market Saving + Record History for Markets --- src/main/java/foxz/utils/Market.java | 72 +++++++++++++++---- .../java/noppes/npcs/PacketHandlerServer.java | 2 + .../client/gui/roles/GuiNpcTraderSetup.java | 22 +++--- .../controllers/PlayerDataController.java | 23 ++---- .../npcs/controllers/data/PlayerData.java | 2 +- .../java/noppes/npcs/roles/RoleTrader.java | 49 +++++++------ .../npcs/scripted/roles/ScriptRoleTrader.java | 6 +- .../java/noppes/npcs/util/CacheHashMap.java | 29 +++++++- .../noppes/npcs/util/CustomNPCsThreader.java | 3 +- .../noppes/npcs/util/MarketCachedObject.java | 15 ++++ .../java/noppes/npcs/util/NBTJsonUtil.java | 18 +++-- src/main/resources/META-INF/customnpcs_at.cfg | 4 ++ .../assets/customnpcs/lang/en_US.lang | 1 + 13 files changed, 172 insertions(+), 74 deletions(-) create mode 100644 src/main/java/noppes/npcs/util/MarketCachedObject.java diff --git a/src/main/java/foxz/utils/Market.java b/src/main/java/foxz/utils/Market.java index 60b345eb9..461cc9a1c 100644 --- a/src/main/java/foxz/utils/Market.java +++ b/src/main/java/foxz/utils/Market.java @@ -9,6 +9,8 @@ import noppes.npcs.LogWriter; import noppes.npcs.config.ConfigMain; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.roles.RoleTrader; +import noppes.npcs.util.CacheHashMap; +import noppes.npcs.util.MarketCachedObject; import noppes.npcs.util.NBTJsonUtil; import java.io.File; @@ -16,28 +18,68 @@ import java.io.FileOutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import static noppes.npcs.controllers.PlayerDataController.loadNBTData; +import static noppes.npcs.util.CustomNPCsThreader.customNPCThread; public class Market { - static public void save(RoleTrader r, String name) { + private static final CacheHashMap marketCache = new CacheHashMap<>(5 * 60 * 1000, 1 * 60 * 1000); + + public static void save(RoleTrader r, String name) { if(name.isEmpty()) return; + + NBTTagCompound roleCompound = r.writeNBT(new NBTTagCompound()); + roleCompound.setString("SaveName", name); + putMarketCache(name, roleCompound); + if(r.recordHistory) + return; + + saveFile(roleCompound); + } + + public static void saveFile(NBTTagCompound compound) { + String name = compound.getString("SaveName"); File file = getFile(name + "_new"); File file1 = getFile(name); + customNPCThread.execute(() -> { + try { + if(ConfigMain.MarketDatFormat){ + CompressedStreamTools.writeCompressed(compound, new FileOutputStream(file)); + } else { + NBTJsonUtil.SaveFile(file, compound); + } + if(file1.exists()){ + file1.delete(); + } + file.renameTo(file1); + } catch (Exception e) { - try { - if(ConfigMain.MarketDatFormat){ - CompressedStreamTools.writeCompressed(r.writeNBT(new NBTTagCompound()), new FileOutputStream(file)); - } else { - NBTJsonUtil.SaveFile(file, r.writeNBT(new NBTTagCompound())); } - if(file1.exists()){ - file1.delete(); - } - file.renameTo(file1); - } catch (Exception e) { + }); + } + public static void getMarket(RoleTrader role, String name){ + NBTTagCompound data = getMarketCache(name); + if(data != null){ + role.readNBT(data); + return; + } + + load(role, name); + } + + public static NBTTagCompound getMarketCache(final String name) { + synchronized (marketCache) { + if (!marketCache.containsKey(name)) { + return null; + } + return marketCache.get(name).getObject(); + } + } + + public static void putMarketCache(final String name, final NBTTagCompound role) { + synchronized (marketCache) { + marketCache.put(name, new MarketCachedObject(role)); } } @@ -51,7 +93,7 @@ public class Market { try { if(ConfigMain.MarketDatFormat){ - NBTTagCompound compound = loadNBTData(file); + NBTTagCompound compound = NBTJsonUtil.loadNBTData(file); role.readNBT(compound); } else { @@ -106,7 +148,7 @@ public class Market { if(!getFile(marketName).exists()) Market.save((RoleTrader) npc.roleInterface, marketName); - Market.load((RoleTrader) npc.roleInterface, marketName); + Market.getMarket((RoleTrader) npc.roleInterface, marketName); } @@ -164,7 +206,7 @@ public class Market { } } else { if(file.getName().endsWith(".dat")){ - compound = loadNBTData(file); + compound = NBTJsonUtil.loadNBTData(file); if(compound.hasKey("PlayerName")){ filename = file.getName().substring(0, file.getName().length() - 4); valid = true; diff --git a/src/main/java/noppes/npcs/PacketHandlerServer.java b/src/main/java/noppes/npcs/PacketHandlerServer.java index fad1036f8..937a42f7a 100644 --- a/src/main/java/noppes/npcs/PacketHandlerServer.java +++ b/src/main/java/noppes/npcs/PacketHandlerServer.java @@ -1094,6 +1094,8 @@ public class PacketHandlerServer{ } else if(type == EnumPacketServer.TraderMarketSave){ String market = Server.readString(buffer); + if(market == null) + return; boolean bo = buffer.readBoolean(); if(npc.roleInterface instanceof RoleTrader){ if(bo) diff --git a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcTraderSetup.java b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcTraderSetup.java index 812a7721f..067699b70 100644 --- a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcTraderSetup.java +++ b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcTraderSetup.java @@ -14,17 +14,17 @@ import noppes.npcs.roles.RoleTrader; import org.lwjgl.opengl.GL11; public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITextfieldListener{ - + private final ResourceLocation slot = new ResourceLocation("customnpcs","textures/gui/slot.png"); private RoleTrader role; - + public GuiNpcTraderSetup(EntityNPCInterface npc, ContainerNPCTraderSetup container){ super(npc, container); ySize = 220; menuYOffset = 10; role = container.role; } - + @Override public void initGui(){ super.initGui(); @@ -35,9 +35,12 @@ public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITex addLabel(new GuiNpcLabel(1, "gui.ignoreDamage", guiLeft + 260, guiTop + 29)); addButton(new GuiNpcButtonYesNo(1, guiLeft + 340, guiTop + 24, role.ignoreDamage)); - + addLabel(new GuiNpcLabel(2, "gui.ignoreNBT", guiLeft + 260, guiTop + 51)); addButton(new GuiNpcButtonYesNo(2, guiLeft + 340, guiTop + 46, role.ignoreNBT)); + + addLabel(new GuiNpcLabel(3, "gui.recordHistory", guiLeft + 260, guiTop + 73)); + addButton(new GuiNpcButtonYesNo(3, guiLeft + 340, guiTop + 68, role.recordHistory)); } @Override @@ -55,6 +58,9 @@ public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITex if(guibutton.id == 2){ role.ignoreNBT = ((GuiNpcButtonYesNo)guibutton).getBoolean(); } + if(guibutton.id == 3){ + role.recordHistory = ((GuiNpcButtonYesNo)guibutton).getBoolean(); + } } @Override @@ -69,20 +75,20 @@ public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITex GL11.glColor4f(1, 1, 1, 1); drawTexturedModalRect(x - 1, y, 0, 0, 18, 18); drawTexturedModalRect(x + 17, y, 0, 0, 18, 18); - + fontRendererObj.drawString("=", x + 36, y + 5, CustomNpcResourceListener.DefaultTextColor); mc.renderEngine.bindTexture(this.slot); GL11.glColor4f(1, 1, 1, 1); drawTexturedModalRect(x + 42, y, 0, 0, 18, 18); } } - + @Override public void save() { Client.sendData(EnumPacketServer.TraderMarketSave, role.marketName, false); Client.sendData(EnumPacketServer.RoleSave, role.writeToNBT(new NBTTagCompound())); } - + @Override public void unFocused(GuiNpcTextField guiNpcTextField) { String name = guiNpcTextField.getText(); @@ -90,6 +96,6 @@ public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITex role.marketName = name; Client.sendData(EnumPacketServer.TraderMarketSave, role.marketName, true); } - + } } diff --git a/src/main/java/noppes/npcs/controllers/PlayerDataController.java b/src/main/java/noppes/npcs/controllers/PlayerDataController.java index aaac92b25..05708cdd5 100644 --- a/src/main/java/noppes/npcs/controllers/PlayerDataController.java +++ b/src/main/java/noppes/npcs/controllers/PlayerDataController.java @@ -28,7 +28,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.GZIPInputStream; -import static noppes.npcs.util.CustomNPCsThreader.playerDataThread; +import static noppes.npcs.util.CustomNPCsThreader.customNPCThread; public class PlayerDataController { public static PlayerDataController Instance; @@ -66,7 +66,7 @@ public class PlayerDataController { } } if(file.getName().endsWith(".dat")){ - NBTTagCompound compound = loadNBTData(file); + NBTTagCompound compound = NBTJsonUtil.loadNBTData(file); if(compound.hasKey("PlayerName")){ map.put(compound.getString("PlayerName"), file.getName().substring(0, file.getName().length() - 4)); } @@ -128,7 +128,7 @@ public class PlayerDataController { } public synchronized void savePlayerDataMap(){ - playerDataThread.execute(() -> { + customNPCThread.execute(() -> { try { File saveDir = CustomNpcs.getWorldSaveDirectory(); File file = new File(saveDir, "playerdatamap.dat_new"); @@ -230,7 +230,7 @@ public class PlayerDataController { File file = new File(saveDir, filename); if(file.exists()){ if(ConfigMain.DatFormat){ - return loadNBTData(file); + return NBTJsonUtil.loadNBTData(file); } else { return NBTJsonUtil.LoadFile(file); } @@ -242,16 +242,7 @@ public class PlayerDataController { return new NBTTagCompound(); } - public static NBTTagCompound loadNBTData(File file){ - try { - return CompressedStreamTools.readCompressed(new FileInputStream(file)); - } catch (Exception e) { - LogWriter.error("Error loading: " + file.getName(), e); - } - return new NBTTagCompound(); - } - - public void putPlayerDataCache(final String uuid, final PlayerData playerCompound) { + public void putPlayerDataCache(final String uuid, final PlayerData playerCompound) { synchronized (playerDataCache) { playerDataCache.put(uuid, new CacheHashMap.CachedObject<>(playerCompound)); } @@ -453,7 +444,7 @@ public class PlayerDataController { } } if(file.getName().endsWith(".dat")){ - NBTTagCompound compound = loadNBTData(file); + NBTTagCompound compound = NBTJsonUtil.loadNBTData(file); if(compound.hasKey("PlayerName")){ map.put(compound.getString("PlayerName"), file.getName().substring(0, file.getName().length() - 4)); } @@ -536,7 +527,7 @@ public class PlayerDataController { } } else { if(file.getName().endsWith(".dat")){ - compound = loadNBTData(file); + compound = NBTJsonUtil.loadNBTData(file); if(compound.hasKey("PlayerName")){ filename = file.getName().substring(0, file.getName().length() - 4); valid = true; diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerData.java b/src/main/java/noppes/npcs/controllers/data/PlayerData.java index 593bd89be..4b12a1507 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerData.java @@ -277,7 +277,7 @@ public class PlayerData implements IExtendedEntityProperties, IPlayerData { } PlayerDataController.Instance.putPlayerMap(playername, uuid); PlayerDataController.Instance.putPlayerDataCache(uuid, this); - CustomNPCsThreader.playerDataThread.execute(() -> { + CustomNPCsThreader.customNPCThread.execute(() -> { try { File saveDir = PlayerDataController.Instance.getSaveDir(); File file = new File(saveDir, filename + "_new"); diff --git a/src/main/java/noppes/npcs/roles/RoleTrader.java b/src/main/java/noppes/npcs/roles/RoleTrader.java index f6406a6e8..fa5ae40bc 100644 --- a/src/main/java/noppes/npcs/roles/RoleTrader.java +++ b/src/main/java/noppes/npcs/roles/RoleTrader.java @@ -15,7 +15,7 @@ import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; -public class RoleTrader extends RoleInterface{ +public class RoleTrader extends RoleInterface { public String marketName = ""; public NpcMiscInventory inventoryCurrency; @@ -23,14 +23,13 @@ public class RoleTrader extends RoleInterface{ public boolean ignoreDamage = false; public boolean ignoreNBT = false; - - public boolean toSave = false; - + public boolean recordHistory = false; + public int[] purchases; public int[] disableSlot; public HashMap playerPurchases; public HashMap playerDisableSlot; - + public RoleTrader(EntityNPCInterface npc) { super(npc); inventoryCurrency = new NpcMiscInventory(36); @@ -45,10 +44,6 @@ public class RoleTrader extends RoleInterface{ public NBTTagCompound writeToNBT(NBTTagCompound nbttagcompound) { nbttagcompound.setString("TraderMarket", marketName); writeNBT(nbttagcompound); - if (toSave && !npc.isRemote()) { - Market.save(this, marketName); - } - toSave = false; return nbttagcompound; } @@ -57,10 +52,13 @@ public class RoleTrader extends RoleInterface{ nbttagcompound.setTag("TraderSold", inventorySold.getToNBT()); nbttagcompound.setBoolean("TraderIgnoreDamage", ignoreDamage); nbttagcompound.setBoolean("TraderIgnoreNBT", ignoreNBT); - nbttagcompound.setIntArray("Purchases", purchases); + nbttagcompound.setBoolean("RecordHistory", recordHistory); nbttagcompound.setIntArray("DisableSlot", disableSlot); - nbttagcompound.setTag("PlayerPurchases", NBTTags.nbtStringIntegerArrayMap(playerPurchases)); nbttagcompound.setTag("PlayerDisableSlot", NBTTags.nbtStringIntegerArrayMap(playerDisableSlot)); + if(recordHistory){ + nbttagcompound.setIntArray("Purchases", purchases); + nbttagcompound.setTag("PlayerPurchases", NBTTags.nbtStringIntegerArrayMap(playerPurchases)); + } return nbttagcompound; } @@ -68,12 +66,11 @@ public class RoleTrader extends RoleInterface{ public void readFromNBT(NBTTagCompound nbttagcompound) { marketName = nbttagcompound.getString("TraderMarket"); readNBT(nbttagcompound); - + try { - Market.load(this, marketName); + Market.getMarket(this, marketName); } catch (Exception ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); - } } @@ -82,25 +79,28 @@ public class RoleTrader extends RoleInterface{ inventorySold.setFromNBT(nbttagcompound.getCompoundTag("TraderSold")); ignoreDamage = nbttagcompound.getBoolean("TraderIgnoreDamage"); ignoreNBT = nbttagcompound.getBoolean("TraderIgnoreNBT"); - purchases = nbttagcompound.getIntArray("Purchases"); + recordHistory = nbttagcompound.getBoolean("RecordHistory"); + disableSlot = nbttagcompound.getIntArray("DisableSlot"); + playerDisableSlot = NBTTags.getStringIntegerArrayMap(nbttagcompound.getTagList("PlayerDisableSlot", 10), 18); + if(recordHistory){ + purchases = nbttagcompound.getIntArray("Purchases"); + playerPurchases = NBTTags.getStringIntegerArrayMap(nbttagcompound.getTagList("PlayerPurchases", 10), 18); + } if (purchases == null || purchases.length != 18) { purchases = new int[18]; for (int i = 0; i < purchases.length; ++i) purchases[i] = 0; } - disableSlot = nbttagcompound.getIntArray("DisableSlot"); if (disableSlot == null || disableSlot.length != 18) { disableSlot = new int[18]; for (int i = 0; i < disableSlot.length; ++i) disableSlot[i] = 0; } - playerPurchases = NBTTags.getStringIntegerArrayMap(nbttagcompound.getTagList("PlayerPurchases", 10), 18); - playerDisableSlot = NBTTags.getStringIntegerArrayMap(nbttagcompound.getTagList("PlayerDisableSlot", 10), 18); } - + @Override public void interact(EntityPlayer player) { npc.say(player, npc.advanced.getInteractLine()); try { - Market.load(this, marketName); + Market.getMarket(this, marketName); } catch (Exception ex) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); } @@ -117,19 +117,22 @@ public class RoleTrader extends RoleInterface{ } return false; } - + public void addPurchase(int slot, String playerName) { if(slot >= 18 || slot < 0) return; ++purchases[slot]; ++getArrayByName(playerName, playerPurchases)[slot]; + + if(recordHistory) + Market.save(this, marketName); } - + public boolean isSlotEnabled(int slot, String playerName) { if(slot >= 18 || slot < 0) return false; if (disableSlot[slot] > 0) return false; return getArrayByName(playerName, playerDisableSlot)[slot] <= 0; } - + public int[] getArrayByName(String name, HashMap map) { map.computeIfAbsent(name, k -> new int[18]); return map.get(name); diff --git a/src/main/java/noppes/npcs/scripted/roles/ScriptRoleTrader.java b/src/main/java/noppes/npcs/scripted/roles/ScriptRoleTrader.java index 1100a6c71..5fcc54821 100644 --- a/src/main/java/noppes/npcs/scripted/roles/ScriptRoleTrader.java +++ b/src/main/java/noppes/npcs/scripted/roles/ScriptRoleTrader.java @@ -30,7 +30,7 @@ public class ScriptRoleTrader extends ScriptRoleInterface implements IRoleTrader role.inventoryCurrency.items.put(slot + 18, currency2.getMCItemStack()); else role.inventoryCurrency.items.remove(slot + 18); - + role.inventorySold.items.put(slot, sold.getMCItemStack()); } @@ -55,7 +55,7 @@ public class ScriptRoleTrader extends ScriptRoleInterface implements IRoleTrader } return currency; } - + public void removeSellOption(int slot){ if(slot >= 18 || slot < 0) return; @@ -66,7 +66,7 @@ public class ScriptRoleTrader extends ScriptRoleInterface implements IRoleTrader public void setMarket(String name){ role.marketName = name; - Market.load(role, name); + Market.getMarket(role, name); } public String getMarket(){ diff --git a/src/main/java/noppes/npcs/util/CacheHashMap.java b/src/main/java/noppes/npcs/util/CacheHashMap.java index 200630327..12e682b61 100644 --- a/src/main/java/noppes/npcs/util/CacheHashMap.java +++ b/src/main/java/noppes/npcs/util/CacheHashMap.java @@ -6,11 +6,17 @@ import java.util.Map; public class CacheHashMap> extends HashMap { private final long maxCacheTime; + private long saveInterval; public CacheHashMap(long maxCacheTime) { this.maxCacheTime = maxCacheTime; } + public CacheHashMap(long maxCacheTime, long saveInterval) { + this.maxCacheTime = maxCacheTime; + this.saveInterval = saveInterval; + } + @Override public V get(Object key) { V object = super.get(key); @@ -19,9 +25,16 @@ public class CacheHashMap> extends Has Iterator> iterator = this.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); - long timeDiff = entry.getValue().timeSinceAccessed(); - if (timeDiff > this.maxCacheTime) { + long lifeTimeDiff = entry.getValue().timeSinceAccessed(); + if (lifeTimeDiff > this.maxCacheTime) { + entry.getValue().save(); iterator.remove(); + } else if (this.saveInterval > 0) { + long saveTimeDiff = entry.getValue().timeSinceSaved(); + if (saveTimeDiff > this.saveInterval) { + entry.getValue().save(); + entry.getValue().updateSaveTime(); + } } } @@ -29,22 +42,34 @@ public class CacheHashMap> extends Has } public static class CachedObject { + private long timeSaved; private long lastTimeAccessed; private final T object; public CachedObject(T object) { this.object = object; this.lastTimeAccessed = System.currentTimeMillis(); + this.timeSaved = System.currentTimeMillis(); } public long timeSinceAccessed() { return System.currentTimeMillis() - this.lastTimeAccessed; } + public long timeSinceSaved() { + return System.currentTimeMillis() - this.timeSaved; + } + public void updateTimeAccessed() { this.lastTimeAccessed = System.currentTimeMillis(); } + public void updateSaveTime() { + this.timeSaved = System.currentTimeMillis(); + } + + public void save(){} + public T getObject() { return this.object; } diff --git a/src/main/java/noppes/npcs/util/CustomNPCsThreader.java b/src/main/java/noppes/npcs/util/CustomNPCsThreader.java index 8522fdf55..cdc040112 100644 --- a/src/main/java/noppes/npcs/util/CustomNPCsThreader.java +++ b/src/main/java/noppes/npcs/util/CustomNPCsThreader.java @@ -2,7 +2,8 @@ package noppes.npcs.util; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; public class CustomNPCsThreader { - public static final Executor playerDataThread = Executors.newSingleThreadExecutor(); + public static final Executor customNPCThread = Executors.newSingleThreadExecutor(); } diff --git a/src/main/java/noppes/npcs/util/MarketCachedObject.java b/src/main/java/noppes/npcs/util/MarketCachedObject.java new file mode 100644 index 000000000..1a6aebfbe --- /dev/null +++ b/src/main/java/noppes/npcs/util/MarketCachedObject.java @@ -0,0 +1,15 @@ +package noppes.npcs.util; + +import foxz.utils.Market; +import net.minecraft.nbt.NBTTagCompound; + +public class MarketCachedObject extends CacheHashMap.CachedObject { + public MarketCachedObject(NBTTagCompound object) { + super(object); + } + + @Override + public void save() { + Market.saveFile(getObject()); + } +} diff --git a/src/main/java/noppes/npcs/util/NBTJsonUtil.java b/src/main/java/noppes/npcs/util/NBTJsonUtil.java index 21104a103..ae8cbc7c2 100644 --- a/src/main/java/noppes/npcs/util/NBTJsonUtil.java +++ b/src/main/java/noppes/npcs/util/NBTJsonUtil.java @@ -3,12 +3,10 @@ package noppes.npcs.util; import com.google.common.io.Files; import cpw.mods.fml.common.ObfuscationReflectionHelper; import net.minecraft.nbt.*; +import noppes.npcs.LogWriter; import org.apache.commons.io.Charsets; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; +import java.io.*; import java.util.ArrayList; import java.util.List; @@ -211,7 +209,17 @@ public class NBTJsonUtil { } return json; } - static class JsonLine{ + + public static NBTTagCompound loadNBTData(File file){ + try { + return CompressedStreamTools.readCompressed(new FileInputStream(file)); + } catch (Exception e) { + LogWriter.error("Error loading: " + file.getName(), e); + } + return new NBTTagCompound(); + } + + static class JsonLine{ private String line; public JsonLine(String line){ this.line = line; diff --git a/src/main/resources/META-INF/customnpcs_at.cfg b/src/main/resources/META-INF/customnpcs_at.cfg index 56f161026..990efc151 100644 --- a/src/main/resources/META-INF/customnpcs_at.cfg +++ b/src/main/resources/META-INF/customnpcs_at.cfg @@ -17,3 +17,7 @@ public net.minecraft.client.renderer.ItemRenderer field_78453_b # itemToRender public net.minecraft.client.renderer.entity.RenderPlayer func_77043_a(Lnet/minecraft/entity/EntityLivingBase;FFF)V # rotateCorpse #EntityLivingBase public net.minecraft.entity.EntityLivingBase func_82166_i()I # getArmSwingAnimationEnd +# Minecraft +public net.minecraft.client.Minecraft field_147126_aw # mcMusicTicker +# MusicTicker +public net.minecraft.client.audio.MusicTicker field_147676_d # timeUntilNextMusic diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 4338c92f1..f929b3bdb 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -87,6 +87,7 @@ gui.untilclose=UntilClose gui.whenavailable=OnTheMove gui.ignoreDamage=Ignore damage gui.ignoreNBT=Ignore NBT +gui.recordHistory=Record History gui.minrange=MinimumRange gui.engagedistance=EngageDistance gui.orbitdistance=OrbitDistance