diff --git a/build.gradle b/build.gradle index 755eef405..d7092be2b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'idea' } -version = "1.10.2.3" +version = "1.10.2" group= "kamkeel.customnpc-plus" archivesBaseName = "CustomNPC-Plus" diff --git a/src/main/java/kamkeel/npcs/controllers/ProfileController.java b/src/main/java/kamkeel/npcs/controllers/ProfileController.java index a8f2bf91c..77ee89578 100644 --- a/src/main/java/kamkeel/npcs/controllers/ProfileController.java +++ b/src/main/java/kamkeel/npcs/controllers/ProfileController.java @@ -97,6 +97,7 @@ public class ProfileController implements IProfileHandler { if (player == null) return; Profile profile; + boolean profileModified = false; if (activeProfiles.containsKey(player.getUniqueID())) { profile = activeProfiles.get(player.getUniqueID()); profile.player = player; @@ -109,6 +110,7 @@ public class ProfileController implements IProfileHandler { profile.getSlots().put(0, defaultSlot); profile.currentSlotId = 0; saveSlotData(player); + profileModified = true; } if (!profile.getSlots().containsKey(profile.currentSlotId)) { profile.currentSlotId = 0; @@ -117,7 +119,10 @@ public class ProfileController implements IProfileHandler { activeProfiles.put(player.getUniqueID(), profile); loadSlotData(player); verifySlotQuests(profile.player); - save(player, profile); + // Avoid saving on every login; only persist if the profile was + // modified (e.g. default slot creation). + if (profileModified) + save(player, profile); } } @@ -341,6 +346,7 @@ public class ProfileController implements IProfileHandler { return ProfileOperation.error(MSG_CANNOT_CLONE_CURRENT); } if (temporary) { + // Use a negative ID for temporary slots so they are never saved to disk. destinationSlotId = getNextAvailableTempSlot(profile); } else { if (destinationSlotId <= 0) { diff --git a/src/main/java/kamkeel/npcs/controllers/data/profile/Profile.java b/src/main/java/kamkeel/npcs/controllers/data/profile/Profile.java index 857d169f6..04ce9b66c 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/profile/Profile.java +++ b/src/main/java/kamkeel/npcs/controllers/data/profile/Profile.java @@ -81,7 +81,8 @@ public class Profile implements IProfile { compound.setInteger("CurrentSlotId", currentSlotId); NBTTagCompound slotsCompound = new NBTTagCompound(); for (Map.Entry entry : slots.entrySet()) { - // Save only non-temporary slots. + // Temporary slots (typically using negative IDs) are kept in memory + // only and skipped here so they never persist to disk. if (entry.getValue().isTemporary()) continue; slotsCompound.setTag(String.valueOf(entry.getKey()), entry.getValue().toNBT()); diff --git a/src/main/java/noppes/npcs/ServerEventsHandler.java b/src/main/java/noppes/npcs/ServerEventsHandler.java index f8333cc2b..c5b92db0a 100644 --- a/src/main/java/noppes/npcs/ServerEventsHandler.java +++ b/src/main/java/noppes/npcs/ServerEventsHandler.java @@ -285,6 +285,16 @@ public class ServerEventsHandler { } } + @SubscribeEvent + public void onPlayerRespawn(cpw.mods.fml.common.gameevent.PlayerEvent.PlayerRespawnEvent event) { + if (event.player == null || event.player.worldObj == null || event.player.worldObj.isRemote) + return; + + PlayerData data = PlayerData.get(event.player); + data.setGUIOpen(false); + data.editingNpc = null; + } + private void doExcalibur(EntityPlayer player, EntityLivingBase entity) { ItemStack item = player.getCurrentEquippedItem(); if (item == null || item.getItem() != CustomItems.excalibur) diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiNpcBiomes.java b/src/main/java/noppes/npcs/client/gui/SubGuiNpcBiomes.java index 76b5d8150..95c11fd27 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiNpcBiomes.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiNpcBiomes.java @@ -2,14 +2,14 @@ package noppes.npcs.client.gui; import net.minecraft.client.gui.GuiButton; import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.BiomeDictionary; import noppes.npcs.client.gui.util.GuiCustomScroll; import noppes.npcs.client.gui.util.GuiNpcButton; import noppes.npcs.client.gui.util.GuiNpcLabel; import noppes.npcs.client.gui.util.SubGuiInterface; import noppes.npcs.controllers.data.SpawnData; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class SubGuiNpcBiomes extends SubGuiInterface { private SpawnData data; @@ -45,7 +45,15 @@ public class SubGuiNpcBiomes extends SubGuiInterface { addLabel(new GuiNpcLabel(2, "spawning.spawningBiomes", guiLeft + 200, guiTop + 4)); List biomes = new ArrayList(); + Set allBiomes = new HashSet(); + for (BiomeDictionary.Type type : BiomeDictionary.Type.values()) { + Collections.addAll(allBiomes, BiomeDictionary.getBiomesForType(type)); + } for (BiomeGenBase base : BiomeGenBase.getBiomeGenArray()) { + if (base != null) + allBiomes.add(base); + } + for (BiomeGenBase base : allBiomes) { if (base != null && base.biomeName != null && !data.biomes.contains(base.biomeName)) { biomes.add(base.biomeName); } @@ -82,7 +90,15 @@ public class SubGuiNpcBiomes extends SubGuiInterface { } if (button.id == 3) { data.biomes.clear(); + Set allBiomes = new HashSet(); + for (BiomeDictionary.Type type : BiomeDictionary.Type.values()) { + Collections.addAll(allBiomes, BiomeDictionary.getBiomesForType(type)); + } for (BiomeGenBase base : BiomeGenBase.getBiomeGenArray()) { + if (base != null) + allBiomes.add(base); + } + for (BiomeGenBase base : allBiomes) { if (base != null) { data.biomes.add(base.biomeName); } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNpcNaturalSpawns.java b/src/main/java/noppes/npcs/client/gui/global/GuiNpcNaturalSpawns.java index ca5b7fe9e..aba615c0e 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNpcNaturalSpawns.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNpcNaturalSpawns.java @@ -66,6 +66,7 @@ public class GuiNpcNaturalSpawns extends GuiNPCInterface2 implements IGuiData, I addButton(new GuiNpcButton(4, guiLeft + 90, y - 5, 50, 20, "selectServer.edit")); addSlider(new GuiNpcSlider(this, 5, guiLeft + 4, y += 17, 180, 20, (float) this.spawn.itemWeight / 100)); + addExtra(new GuiHoverText(1, "spawning.naturalInfo", guiLeft + 188, y + 5)); this.spawnEntryScroll.guiLeft = guiLeft + 4; this.spawnEntryScroll.guiTop = y + 40; diff --git a/src/main/java/noppes/npcs/scripted/NpcAPI.java b/src/main/java/noppes/npcs/scripted/NpcAPI.java index 363c75d4e..86e237352 100644 --- a/src/main/java/noppes/npcs/scripted/NpcAPI.java +++ b/src/main/java/noppes/npcs/scripted/NpcAPI.java @@ -32,6 +32,8 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.common.BiomeDictionary.Type; import noppes.npcs.CustomNpcs; import noppes.npcs.NoppesUtilServer; import noppes.npcs.api.*; @@ -277,7 +279,15 @@ public class NpcAPI extends AbstractNpcAPI { @Override public String[] getAllBiomeNames() { List biomes = new ArrayList<>(); + Set allBiomes = new HashSet(); + for (Type type : BiomeDictionary.Type.values()) { + Collections.addAll(allBiomes, BiomeDictionary.getBiomesForType(type)); + } for (BiomeGenBase base : BiomeGenBase.getBiomeGenArray()) { + if (base != null) + allBiomes.add(base); + } + for (BiomeGenBase base : allBiomes) { if (base != null && base.biomeName != null) { biomes.add(base.biomeName); } diff --git a/src/main/java/noppes/npcs/util/CustomNPCsThreader.java b/src/main/java/noppes/npcs/util/CustomNPCsThreader.java index 3019fb5b1..6ed04819d 100644 --- a/src/main/java/noppes/npcs/util/CustomNPCsThreader.java +++ b/src/main/java/noppes/npcs/util/CustomNPCsThreader.java @@ -4,5 +4,10 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class CustomNPCsThreader { + /** + * Executor used for all asynchronous profile and player data saves. + * It is single-threaded so submitted tasks run sequentially and the + * save order for a given player is preserved. + */ public static final Executor customNPCThread = Executors.newSingleThreadExecutor(); } diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 92f6c1c8e..ad733fa6b 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -405,6 +405,7 @@ spawning.liquidSpawning=Spawns in Liquid spawning.airSpawning=Spawns in Air spawning.minHeight=Minimum Height spawning.maxHeight=Maximum Height +spawning.naturalInfo=Add at least one spawn entry and configure spawn options for the NPC to spawn naturally role.name=Role role.none=No Role