mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-08 12:34:50 +00:00
customizable registries (#25)
* add a bit javadoc Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> * customizable registries Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> * add gui for customizing registries * add a bit javadoc Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> * spotless Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> * remove debug statements Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> * add notes on localization Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> * update * fix * fix --------- Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> Co-authored-by: Dream Master <dream-master@gmx.net>
This commit is contained in:
1 parent
03e84e3d37
commit
3d150d95cb
17 files changed
+1387
-11
No files matched your search
@@ -2,20 +2,24 @@ package com.gtnewhorizon.structurelib;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityClientPlayerMP;
|
||||
import net.minecraft.client.gui.GuiNewChat;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.culling.Frustrum;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -28,18 +32,27 @@ import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.config.ConfigElement;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
|
||||
import cpw.mods.fml.client.config.GuiConfig;
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||
import cpw.mods.fml.common.network.FMLNetworkEvent;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
|
||||
@@ -251,6 +264,47 @@ public class ClientProxy extends CommonProxy {
|
||||
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
|
||||
}
|
||||
|
||||
static void sendRegistryOrderToServer() {
|
||||
if (FMLEventHandler.configSent) return;
|
||||
FMLEventHandler.configSent = true;
|
||||
for (String s : SortedRegistry.ALL_REGISTRIES.keySet()) {
|
||||
Pair<List<String>, List<String>> registryOrder = ConfigurationHandler.INSTANCE.getRegistryOrder(s);
|
||||
if (registryOrder == null || registryOrder.getKey().isEmpty() && registryOrder.getValue().isEmpty())
|
||||
continue;
|
||||
StructureLib.net
|
||||
.sendToServer(new RegistryOrderSyncMessage(s, registryOrder.getKey(), registryOrder.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadComplete(FMLLoadCompleteEvent evt) {
|
||||
super.loadComplete(evt);
|
||||
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
|
||||
SortedRegistry<?> r = e.getValue().get();
|
||||
if (r == null) continue;
|
||||
Pair<List<String>, List<String>> p = ConfigurationHandler.INSTANCE.getRegistryOrder(e.getKey());
|
||||
r.registerOrdering(getSPPlayerUUID(), p.getKey(), p.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getSPPlayerUUID() {
|
||||
return EntityPlayer.func_146094_a(Minecraft.getMinecraft().getSession().func_148256_e());
|
||||
}
|
||||
|
||||
public void displayConfigGUI(String category) {
|
||||
ConfigElement<Object> element = new ConfigElement<>(
|
||||
ConfigurationHandler.INSTANCE.getConfig().getCategory(category));
|
||||
GuiConfig guiConfig = new GuiConfig(
|
||||
null,
|
||||
element.getChildElements(),
|
||||
StructureLibAPI.MOD_ID,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
I18n.format(element.getLanguageKey()));
|
||||
Minecraft.getMinecraft().displayGuiScreen(guiConfig);
|
||||
}
|
||||
|
||||
private static class HintGroup {
|
||||
|
||||
private final List<HintParticleInfo> hints = new LinkedList<>();
|
||||
@@ -439,12 +493,34 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
public static class FMLEventHandler {
|
||||
|
||||
static boolean connected = false;
|
||||
static boolean configSent = false;
|
||||
|
||||
private void resetPlayerLocation() {
|
||||
lastPlayerPos.xCoord = Minecraft.getMinecraft().thePlayer.posX;
|
||||
lastPlayerPos.yCoord = Minecraft.getMinecraft().thePlayer.posY;
|
||||
lastPlayerPos.zCoord = Minecraft.getMinecraft().thePlayer.posZ;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerLogIn(FMLNetworkEvent.ClientConnectedToServerEvent e) {
|
||||
connected = true;
|
||||
configSent = false;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerLogOut(FMLNetworkEvent.ClientDisconnectionFromServerEvent e) {
|
||||
connected = false;
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onConfigChange(ConfigChangedEvent.PostConfigChangedEvent e) {
|
||||
if (connected && e.modID.equals(StructureLibAPI.MOD_ID)) {
|
||||
configSent = false;
|
||||
sendRegistryOrderToServer();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onClientTick(ClientTickEvent e) {
|
||||
if (e.phase == Phase.END && Minecraft.getMinecraft().thePlayer != null) {
|
||||
@@ -488,6 +564,13 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
public static class ForgeEventHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onEntityJoinWorld(EntityJoinWorldEvent e) {
|
||||
if (e.world.isRemote && e.entity instanceof EntityClientPlayerMP) {
|
||||
sendRegistryOrderToServer();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onWorldLoad(WorldEvent.Load e) {
|
||||
if (e.world.isRemote) {
|
||||
|
||||
@@ -12,14 +12,23 @@ import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
|
||||
public class CommonProxy {
|
||||
|
||||
public CommonProxy() {
|
||||
super();
|
||||
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
|
||||
}
|
||||
|
||||
public void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {}
|
||||
|
||||
public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {}
|
||||
@@ -70,6 +79,10 @@ public class CommonProxy {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadComplete(FMLLoadCompleteEvent e) {
|
||||
ConfigurationHandler.INSTANCE.loadRegistryOrder();
|
||||
}
|
||||
|
||||
private final Map<EntityPlayerMP, Map<Object, Long>> throttleMap = new WeakHashMap<>();
|
||||
|
||||
public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
|
||||
@@ -94,4 +107,22 @@ public class CommonProxy {
|
||||
if (!forceUpdateLastSend) submap.put(throttleKey, now);
|
||||
}
|
||||
}
|
||||
|
||||
public void displayConfigGUI(String category) {}
|
||||
|
||||
public static class ForgeEventHandler {
|
||||
|
||||
private int ticksSinceLastPurge;
|
||||
|
||||
@SubscribeEvent
|
||||
public void onServerTickEnd(TickEvent.ServerTickEvent e) {
|
||||
if (e.phase != TickEvent.Phase.END) return;
|
||||
if (++ticksSinceLastPurge < 20 * 60 * 10) return;
|
||||
ticksSinceLastPurge = 0;
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
// it's hard to tell why server could be null in a server tick handler, but who knows?
|
||||
if (server == null) return;
|
||||
SortedRegistry.cleanup(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,26 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraftforge.common.config.ConfigCategory;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
import net.minecraftforge.common.config.Property;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
|
||||
public enum ConfigurationHandler {
|
||||
|
||||
@@ -22,6 +33,7 @@ public enum ConfigurationHandler {
|
||||
private int hintTransparency;
|
||||
private int autoPlaceBudget;
|
||||
private int autoPlaceInterval;
|
||||
private Map<String, Pair<List<String>, List<String>>> registryOrders;
|
||||
|
||||
ConfigurationHandler() {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
@@ -86,8 +98,50 @@ public enum ConfigurationHandler {
|
||||
+ "Note this relates to the wall clock, not in game ticks.\n"
|
||||
+ "Value smaller than default is likely to be perceived as no minimal interval whatsoever.");
|
||||
|
||||
loadRegistryOrder();
|
||||
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
void loadRegistryOrder() {
|
||||
loadRegistryOrderImpl();
|
||||
setLanguageKeys();
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
private void loadRegistryOrderImpl() {
|
||||
registryOrders = new HashMap<>();
|
||||
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
|
||||
SortedRegistry<?> r = e.getValue().get();
|
||||
if (r == null) continue;
|
||||
String category = "registries." + e.getKey();
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
// only meaningful on client, and would crash on server
|
||||
config.setCategoryConfigEntryClass(category, RegistryOrderEntry.class);
|
||||
}
|
||||
Property pOrder = config.get(
|
||||
category,
|
||||
"ordering",
|
||||
Iterables.toArray(r.getCurrentOrdering(), String.class),
|
||||
"stuff not in this list will be automatically available after all entries listed here in their natural order, unless explicitly disabled in disabled config below.");
|
||||
Property pDisable = config.get(category, "disabled", new String[0], "stuff in this list will be disabled");
|
||||
List<String> all = Lists.newArrayList(r.getCurrentOrdering());
|
||||
List<String> curVal = new ArrayList<>(Arrays.asList(pOrder.getStringList()));
|
||||
List<String> disabled = new ArrayList<>(Arrays.asList(pDisable.getStringList()));
|
||||
curVal.removeAll(disabled);
|
||||
all.removeAll(disabled);
|
||||
curVal.removeIf(s -> !all.remove(s));
|
||||
curVal.addAll(all);
|
||||
pOrder.set(curVal.toArray(new String[0]));
|
||||
registryOrders.put(e.getKey(), Pair.of(curVal, disabled));
|
||||
}
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
private void saveConfig() {
|
||||
if (config.hasChanged()) {
|
||||
config.save();
|
||||
config.load();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +176,12 @@ public enum ConfigurationHandler {
|
||||
return autoPlaceInterval;
|
||||
}
|
||||
|
||||
public Pair<List<String>, List<String>> getRegistryOrder(String name) {
|
||||
return registryOrders.get(name);
|
||||
}
|
||||
|
||||
Configuration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
* Forge Mod Loader Copyright (c) 2012-2014 cpw. All rights reserved. This program and the accompanying materials are
|
||||
* made available under the terms of the GNU Lesser Public License v2.1 which accompanies this distribution, and is
|
||||
* available at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html Contributors (this class): bspkrs - implementation
|
||||
*/
|
||||
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import static cpw.mods.fml.client.config.GuiUtils.RESET_CHAR;
|
||||
import static cpw.mods.fml.client.config.GuiUtils.UNDO_CHAR;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiListExtended;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.client.config.GuiButtonExt;
|
||||
import cpw.mods.fml.client.config.GuiConfig;
|
||||
import cpw.mods.fml.client.config.GuiUnicodeGlyphButton;
|
||||
import cpw.mods.fml.client.config.GuiUtils;
|
||||
import cpw.mods.fml.client.config.HoverChecker;
|
||||
import cpw.mods.fml.client.config.IConfigElement;
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent.PostConfigChangedEvent;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.eventhandler.Event.Result;
|
||||
|
||||
/**
|
||||
* This class is the base GuiScreen for all config GUI screens. It can be extended by mods to provide the top-level
|
||||
* config screen that will be called when the Config button is clicked from the Main Menu Mods list.
|
||||
*
|
||||
* @author bspkrs
|
||||
*/
|
||||
public class GuiEditRegistryConfig extends GuiScreen {
|
||||
|
||||
public static final String OMITTED = "..";
|
||||
public static final int LIST_WINDOW_PADDING = 20;
|
||||
public static final int MID_SECTION_WIDTH = 10;
|
||||
/**
|
||||
* A reference to the screen object that created this. Used for navigating between screens.
|
||||
*/
|
||||
public final GuiScreen parentScreen;
|
||||
private final IConfigElement<String> disabledElement;
|
||||
private final String[] beforeDisabledValue;
|
||||
private final IConfigElement<String> orderElement;
|
||||
private final String[] beforeOrderValue;
|
||||
final Collection<EntriesList> lists;
|
||||
private final String registryName;
|
||||
EntriesList disabledEntries;
|
||||
EntriesList orderEntries;
|
||||
public String title = "Config GUI";
|
||||
public String titleLine2;
|
||||
private GuiButtonExt btnDefault;
|
||||
private GuiButtonExt btnUndoAll;
|
||||
public final String modID;
|
||||
/**
|
||||
* When set to a non-null value the OnConfigChanged and PostConfigChanged events will be posted when the Done button
|
||||
* is pressed if any configElements were changed (includes child screens). If not defined, the events will be posted
|
||||
* if the parent gui is null or if the parent gui is not an instance of GuiConfig.
|
||||
*/
|
||||
public final String configID;
|
||||
public final boolean isWorldRunning;
|
||||
public final boolean allRequireWorldRestart;
|
||||
public final boolean allRequireMcRestart;
|
||||
public boolean needsRefresh = true;
|
||||
private HoverChecker undoHoverChecker;
|
||||
private HoverChecker resetHoverChecker;
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public GuiEditRegistryConfig(GuiScreen parentScreen, IConfigElement disabled, IConfigElement order,
|
||||
String registryName, String modID, String configID, boolean allRequireWorldRestart,
|
||||
boolean allRequireMcRestart, String title, String titleLine2) {
|
||||
this.mc = parentScreen.mc;
|
||||
this.lists = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
this.disabledElement = disabled;
|
||||
this.beforeDisabledValue = (String[]) this.disabledElement.getList();
|
||||
this.orderElement = order;
|
||||
this.beforeOrderValue = (String[]) this.orderElement.getList();
|
||||
this.parentScreen = parentScreen;
|
||||
this.allRequireWorldRestart = allRequireWorldRestart;
|
||||
this.allRequireMcRestart = allRequireMcRestart;
|
||||
this.registryName = registryName;
|
||||
this.modID = modID;
|
||||
this.configID = configID;
|
||||
this.isWorldRunning = mc.theWorld != null;
|
||||
if (title != null) this.title = title;
|
||||
this.titleLine2 = titleLine2;
|
||||
if (this.titleLine2 != null && this.titleLine2.startsWith(" > "))
|
||||
this.titleLine2 = this.titleLine2.replaceFirst(" > ", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the buttons (and other controls) to the screen in question.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void initGui() {
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
if (this.disabledEntries == null || this.needsRefresh) {
|
||||
this.disabledEntries = new EntriesList(
|
||||
(width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING,
|
||||
LIST_WINDOW_PADDING,
|
||||
I18n.format("structurelib.configgui.disabled"));
|
||||
this.disabledEntries.initEntries(Arrays.asList(disabledElement.getList()));
|
||||
} else {
|
||||
this.disabledEntries.reinit((width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING, LIST_WINDOW_PADDING);
|
||||
}
|
||||
if (this.orderEntries == null || this.needsRefresh) {
|
||||
this.orderEntries = new EntriesList(
|
||||
(width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING,
|
||||
(width + MID_SECTION_WIDTH) / 2,
|
||||
I18n.format("structurelib.configgui.enabled"));
|
||||
this.orderEntries.initEntries(Arrays.asList(orderElement.getList()));
|
||||
} else {
|
||||
this.orderEntries
|
||||
.reinit((width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING, (width + MID_SECTION_WIDTH) / 2);
|
||||
}
|
||||
this.needsRefresh = false;
|
||||
|
||||
int undoGlyphWidth = mc.fontRenderer.getStringWidth(UNDO_CHAR) * 2;
|
||||
int resetGlyphWidth = mc.fontRenderer.getStringWidth(RESET_CHAR) * 2;
|
||||
int doneWidth = Math.max(mc.fontRenderer.getStringWidth(I18n.format("gui.done")) + 20, 100);
|
||||
int undoWidth = mc.fontRenderer.getStringWidth(" " + I18n.format("fml.configgui.tooltip.undoChanges"))
|
||||
+ undoGlyphWidth
|
||||
+ 20;
|
||||
int resetWidth = mc.fontRenderer.getStringWidth(" " + I18n.format("fml.configgui.tooltip.resetToDefault"))
|
||||
+ resetGlyphWidth
|
||||
+ 20;
|
||||
int buttonWidthHalf = (doneWidth + 5 + undoWidth + 5 + resetWidth + 5) / 2;
|
||||
this.buttonList.add(
|
||||
new GuiButtonExt(
|
||||
2000,
|
||||
this.width / 2 - buttonWidthHalf,
|
||||
this.height - 29,
|
||||
doneWidth,
|
||||
20,
|
||||
I18n.format("gui.done")));
|
||||
this.buttonList.add(
|
||||
this.btnDefault = new GuiUnicodeGlyphButton(
|
||||
2001,
|
||||
this.width / 2 - buttonWidthHalf + doneWidth + 5 + undoWidth + 5,
|
||||
this.height - 29,
|
||||
resetWidth,
|
||||
20,
|
||||
" " + I18n.format("fml.configgui.tooltip.resetToDefault"),
|
||||
RESET_CHAR,
|
||||
2.0F));
|
||||
this.buttonList.add(
|
||||
btnUndoAll = new GuiUnicodeGlyphButton(
|
||||
2002,
|
||||
this.width / 2 - buttonWidthHalf + doneWidth + 5,
|
||||
this.height - 29,
|
||||
undoWidth,
|
||||
20,
|
||||
" " + I18n.format("fml.configgui.tooltip.undoChanges"),
|
||||
UNDO_CHAR,
|
||||
2.0F));
|
||||
|
||||
this.undoHoverChecker = new HoverChecker(this.btnUndoAll, 800);
|
||||
this.resetHoverChecker = new HoverChecker(this.btnDefault, 800);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the screen is unloaded. Used to disable keyboard repeat events
|
||||
*/
|
||||
@Override
|
||||
public void onGuiClosed() {
|
||||
if (this.configID != null && this.parentScreen instanceof GuiConfig) {
|
||||
GuiConfig parentGuiConfig = (GuiConfig) this.parentScreen;
|
||||
parentGuiConfig.needsRefresh = true;
|
||||
parentGuiConfig.initGui();
|
||||
}
|
||||
|
||||
if (!(this.parentScreen instanceof GuiConfig)) Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
public boolean saveConfigElements() {
|
||||
if (isChanged()) {
|
||||
disabledElement.set(disabledEntries.entries.stream().map(e -> e.name).toArray(String[]::new));
|
||||
orderElement.set(orderEntries.entries.stream().map(e -> e.name).toArray(String[]::new));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button) {
|
||||
if (button.id == 2000) {
|
||||
try {
|
||||
if ((configID != null || this.parentScreen == null || !(this.parentScreen instanceof GuiConfig))
|
||||
&& isChanged()) {
|
||||
saveConfigElements();
|
||||
if (Loader.isModLoaded(modID)) {
|
||||
ConfigChangedEvent event = new OnConfigChangedEvent(modID, configID, isWorldRunning, false);
|
||||
FMLCommonHandler.instance().bus().post(event);
|
||||
if (!event.getResult().equals(Result.DENY)) FMLCommonHandler.instance().bus()
|
||||
.post(new PostConfigChangedEvent(modID, configID, isWorldRunning, false));
|
||||
|
||||
if (this.parentScreen instanceof GuiConfig) ((GuiConfig) this.parentScreen).needsRefresh = true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.mc.displayGuiScreen(this.parentScreen);
|
||||
} else if (button.id == 2001) {
|
||||
setToDefault();
|
||||
} else if (button.id == 2002) {
|
||||
undoChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return disabledEntries != null && orderEntries != null;
|
||||
}
|
||||
|
||||
public void undoChanges() {
|
||||
disabledEntries.initEntries(Arrays.asList(beforeDisabledValue));
|
||||
orderEntries.initEntries(Arrays.asList(beforeOrderValue));
|
||||
}
|
||||
|
||||
public void setToDefault() {
|
||||
disabledEntries.initEntries(Arrays.asList(disabledElement.getList()));
|
||||
orderEntries.initEntries(Arrays.asList(orderElement.getList()));
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return !orderEntries.areContentEqual(orderElement.getDefaults())
|
||||
|| !disabledEntries.areContentEqual(disabledElement.getDefaults());
|
||||
}
|
||||
|
||||
public boolean isChanged() {
|
||||
return !orderEntries.areContentEqual(beforeOrderValue) || !disabledEntries.areContentEqual(beforeDisabledValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mouse is clicked.
|
||||
*/
|
||||
@Override
|
||||
protected void mouseClicked(int x, int y, int mouseEvent) {
|
||||
if (mouseEvent != 0 || !this.disabledEntries.func_148179_a(x, y, mouseEvent)
|
||||
&& !this.orderEntries.func_148179_a(x, y, mouseEvent)) {
|
||||
super.mouseClicked(x, y, mouseEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mouse is moved or a mouse button is released. Signature: (mouseX, mouseY, which) which==-1 is
|
||||
* mouseMove, which==0 or which==1 is mouseUp
|
||||
*/
|
||||
@Override
|
||||
protected void mouseMovedOrUp(int x, int y, int mouseEvent) {
|
||||
if (mouseEvent != 0 || !this.disabledEntries.func_148181_b(x, y, mouseEvent)
|
||||
&& !this.orderEntries.func_148181_b(x, y, mouseEvent)) {
|
||||
super.mouseMovedOrUp(x, y, mouseEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a key is typed. This is the equivalent of KeyListener.keyTyped(KeyEvent e).
|
||||
*/
|
||||
@Override
|
||||
protected void keyTyped(char eventChar, int eventKey) {
|
||||
if (eventKey == Keyboard.KEY_ESCAPE) this.mc.displayGuiScreen(parentScreen);
|
||||
}
|
||||
|
||||
String trimStringToWidth(String str, int width) {
|
||||
int strwidth = mc.fontRenderer.getStringWidth(str);
|
||||
int elipsisWidth = mc.fontRenderer.getStringWidth(OMITTED);
|
||||
if (strwidth > width - elipsisWidth && elipsisWidth > strwidth) {
|
||||
return mc.fontRenderer.trimStringToWidth(str, width - elipsisWidth).trim() + OMITTED;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the screen and all the components in it.
|
||||
*/
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||
for (EntriesList list : lists) {
|
||||
list.handleDrag(mouseX, mouseY, partialTicks);
|
||||
}
|
||||
this.drawDefaultBackground();
|
||||
for (EntriesList list : lists) {
|
||||
list.drawScreen(mouseX, mouseY, partialTicks);
|
||||
}
|
||||
this.drawCenteredString(this.fontRendererObj, this.title, this.width / 2, 8, 16777215);
|
||||
String title2 = this.titleLine2;
|
||||
|
||||
if (title2 != null) {
|
||||
title2 = trimStringToWidth(title2, width - 6);
|
||||
this.drawCenteredString(this.fontRendererObj, title2, this.width / 2, 18, 16777215);
|
||||
}
|
||||
|
||||
this.btnUndoAll.enabled = isChanged();
|
||||
this.btnDefault.enabled = isDefault();
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
for (EntriesList list : lists) {
|
||||
list.drawScreenPost(mouseX, mouseY, partialTicks);
|
||||
}
|
||||
if (this.undoHoverChecker.checkHover(mouseX, mouseY)) this.drawToolTip(
|
||||
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.undoAll"), 300),
|
||||
mouseX,
|
||||
mouseY);
|
||||
if (this.resetHoverChecker.checkHover(mouseX, mouseY)) this.drawToolTip(
|
||||
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.resetAll"), 300),
|
||||
mouseX,
|
||||
mouseY);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void drawToolTip(List stringList, int x, int y) {
|
||||
this.func_146283_a(stringList, x, y);
|
||||
}
|
||||
|
||||
private class EntriesList extends GuiListExtended {
|
||||
|
||||
final String heading;
|
||||
final List<ListEntry> entries = new ArrayList<>();
|
||||
int controlX;
|
||||
|
||||
public EntriesList(int width, int left, String heading) {
|
||||
super(
|
||||
mc,
|
||||
width,
|
||||
GuiEditRegistryConfig.this.height,
|
||||
titleLine2 != null ? 33 : 23,
|
||||
GuiEditRegistryConfig.this.height - 32,
|
||||
20);
|
||||
this.heading = heading;
|
||||
if (heading != null) {
|
||||
setHasListHeader(true, mc.fontRenderer.FONT_HEIGHT + 4);
|
||||
}
|
||||
setSlotXBoundsFromLeft(left);
|
||||
this.controlX = this.getScrollBarX() - 20;
|
||||
lists.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int p_148128_1_, int p_148128_2_, float p_148128_3_) {
|
||||
GL11.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
ScaledResolution scaledresolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
GL11.glScissor(
|
||||
(left - 4) * scaledresolution.getScaleFactor(),
|
||||
(int) ((scaledresolution.getScaledHeight_double() - (bottom + 4))
|
||||
* scaledresolution.getScaleFactor()),
|
||||
(width + 8) * scaledresolution.getScaleFactor(),
|
||||
(height + 8) * scaledresolution.getScaleFactor());
|
||||
super.drawScreen(p_148128_1_, p_148128_2_, p_148128_3_);
|
||||
GL11.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overlayBackground(int p_148136_1_, int p_148136_2_, int p_148136_3_, int p_148136_4_) {}
|
||||
|
||||
@Override
|
||||
protected void drawContainerBackground(Tessellator tessellator) {
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 0, 1);
|
||||
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glShadeModel(GL11.GL_SMOOTH);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorRGBA_I(0, 64);
|
||||
tessellator.addVertex(left, bottom, 0);
|
||||
tessellator.addVertex(right, bottom, 0);
|
||||
tessellator.addVertex(right, top, 0);
|
||||
tessellator.addVertex(left, top, 0);
|
||||
tessellator.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glShadeModel(GL11.GL_FLAT);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawListHeader(int p_148129_1_, int p_148129_2_, Tessellator p_148129_3_) {
|
||||
if (heading == null) {
|
||||
return;
|
||||
}
|
||||
FontRenderer fr = mc.fontRenderer;
|
||||
fr.drawString(
|
||||
this.heading,
|
||||
p_148129_1_ + this.width / 2 - fr.getStringWidth(this.heading) / 2,
|
||||
Math.min(this.top + 3, p_148129_2_),
|
||||
0xffffff);
|
||||
}
|
||||
|
||||
void reinit(int width, int left) {
|
||||
this.width = width;
|
||||
this.bottom = GuiEditRegistryConfig.this.height - 32;
|
||||
this.setSlotXBoundsFromLeft(left);
|
||||
this.controlX = this.getScrollBarX() - 20;
|
||||
}
|
||||
|
||||
void initEntries(Iterable<?> entries) {
|
||||
this.controlX = this.getScrollBarX() - 20;
|
||||
this.entries.clear();
|
||||
for (Object entry : entries) {
|
||||
addEntry(new ListEntry(entry.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void addEntry(ListEntry entry) {
|
||||
entry.entriesList = this;
|
||||
this.entries.add(entry);
|
||||
this.controlX = Math.min(this.controlX, this.getScrollBarX() - entry.getControlSetWidth() - 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IGuiListEntry getListEntry(int i) {
|
||||
return entries.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSize() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
int getLabelX() {
|
||||
return left + 2;
|
||||
}
|
||||
|
||||
int getControlX() {
|
||||
return controlX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getListWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getScrollBarX() {
|
||||
return left + getListWidth() - 6;
|
||||
}
|
||||
|
||||
public void handleDrag(int mouseX, int mouseY, float partialTicks) {
|
||||
for (int i = 0, entriesSize = entries.size(); i < entriesSize; i++) {
|
||||
ListEntry entry = entries.get(i);
|
||||
if (entry.handleDrag(i, mouseX, mouseY, partialTicks)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void drawScreenPost(int mouseX, int mouseY, float partialTicks) {
|
||||
for (ListEntry entry : entries) {
|
||||
entry.drawToolTip(mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areContentEqual(Object[] vals) {
|
||||
if (vals.length != entries.size()) return false;
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
ListEntry entry = entries.get(i);
|
||||
if (!entry.name.equals(vals[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class ListEntry implements GuiListExtended.IGuiListEntry {
|
||||
|
||||
private EntriesList entriesList;
|
||||
private final HoverChecker hoverCheckerText = new HoverChecker(0, 0, 0, 0, 0);
|
||||
final String name;
|
||||
final String localizedName;
|
||||
private final String tooltip;
|
||||
public boolean modified = false;
|
||||
|
||||
private final GuiButtonExt btnDrag = new GuiButtonDrag(1, 0, 0, 20, 20);
|
||||
private final HoverChecker hoverCheckerBtnDrag = new HoverChecker(btnDrag, 800);
|
||||
private boolean isDragging = false;
|
||||
private long lastScroll = 0;
|
||||
|
||||
ListEntry(String name) {
|
||||
this.name = name;
|
||||
String nameKey = "structurelib.sortedregistry." + registryName + "." + name;
|
||||
this.localizedName = trans(nameKey, name);
|
||||
|
||||
String tooltipKey = nameKey + ".tooltip";
|
||||
String tooltip = trans(tooltipKey, null);
|
||||
StringBuilder tooltipBuilder = new StringBuilder();
|
||||
tooltipBuilder.append(EnumChatFormatting.GREEN);
|
||||
tooltipBuilder.append(this.localizedName);
|
||||
if (tooltip != null) {
|
||||
tooltipBuilder.append('\n').append(EnumChatFormatting.YELLOW).append(tooltip);
|
||||
}
|
||||
tooltipBuilder.append('\n');
|
||||
tooltipBuilder.append(EnumChatFormatting.DARK_GRAY);
|
||||
tooltipBuilder.append(this.name);
|
||||
this.tooltip = tooltipBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, Tessellator tessellator,
|
||||
int mouseX, int mouseY, boolean isSelected) {
|
||||
drawOutlineBox(x, y, listWidth, slotHeight, tessellator);
|
||||
drawLabel(y, slotHeight);
|
||||
drawControlSet(y, slotHeight, mouseX, mouseY);
|
||||
}
|
||||
|
||||
private void drawOutlineBox(int x, int y, int listWidth, int slotHeight, Tessellator tessellator) {
|
||||
if (isDragging) {
|
||||
// left x, right x, top y, bottom y
|
||||
int lx = x, rx = entriesList.getScrollBarX() - 4, ty = y, by = y + slotHeight;
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorOpaque_I(0x808080);
|
||||
tessellator.addVertexWithUV(lx - 4, by + 4, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(rx + 4, by + 4, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(rx + 4, ty - 4, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(lx - 4, ty - 4, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.setColorOpaque_I(0x202020);
|
||||
tessellator.addVertexWithUV(lx - 2, by + 2, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(rx + 2, by + 2, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(rx + 2, ty - 2, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(lx - 2, ty - 2, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawControlSet(int y, int slotHeight, int mouseX, int mouseY) {
|
||||
btnDrag.xPosition = entriesList.getControlX();
|
||||
btnDrag.yPosition = y + (slotHeight - btnDrag.height) / 2;
|
||||
btnDrag.drawButton(mc, mouseX, mouseY);
|
||||
}
|
||||
|
||||
private void drawLabel(int y, int slotHeight) {
|
||||
String label = modified ? EnumChatFormatting.ITALIC + localizedName : localizedName;
|
||||
int allowedWidth = entriesList.getControlX() - entriesList.getLabelX() - 4;
|
||||
// probably not very wise to recompute this every render tick, but it doesn't matter for now
|
||||
String toDraw = trimStringToWidth(label, allowedWidth);
|
||||
hoverCheckerText.updateBounds(
|
||||
y + slotHeight / 2 - mc.fontRenderer.FONT_HEIGHT / 2,
|
||||
y + slotHeight / 2 + mc.fontRenderer.FONT_HEIGHT / 2,
|
||||
entriesList.getLabelX(),
|
||||
entriesList.getLabelX() + mc.fontRenderer.getStringWidth(toDraw));
|
||||
|
||||
mc.fontRenderer.drawString(
|
||||
toDraw,
|
||||
entriesList.getLabelX(),
|
||||
y + slotHeight / 2 - mc.fontRenderer.FONT_HEIGHT / 2,
|
||||
getLabelColor());
|
||||
}
|
||||
|
||||
void drawToolTip(int mouseX, int mouseY) {
|
||||
// ensure checkHover() is always called, or the hover threshold might fail under certain corner cases
|
||||
if (hoverCheckerText.checkHover(mouseX, mouseY)) {
|
||||
GuiEditRegistryConfig.this.drawToolTip(
|
||||
mc.fontRenderer.listFormattedStringToWidth(tooltip, entriesList.getListWidth()),
|
||||
mouseX,
|
||||
mouseY);
|
||||
}
|
||||
if (hoverCheckerBtnDrag.checkHover(mouseX, mouseY) && !isDragging) {
|
||||
GuiEditRegistryConfig.this.drawToolTip(
|
||||
mc.fontRenderer.listFormattedStringToWidth(
|
||||
I18n.format("structurelib.configgui.drag.tooltip"),
|
||||
entriesList.getListWidth()),
|
||||
mouseX,
|
||||
mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getLabelColor() {
|
||||
return isDragging ? 0x888888 : 0xffffff;
|
||||
}
|
||||
|
||||
int getControlSetWidth() {
|
||||
return this.btnDrag.getButtonWidth();
|
||||
}
|
||||
|
||||
public boolean handleDrag(int index, int mouseX, int mouseY, float partialTicks) {
|
||||
if (!isDragging) {
|
||||
return false;
|
||||
}
|
||||
for (EntriesList list : lists) {
|
||||
if (list == entriesList) continue;
|
||||
if (mouseX > list.left && mouseX < list.right && mouseY > list.top && mouseY < list.bottom) {
|
||||
int l;
|
||||
if (list.entries.isEmpty()) l = 0;
|
||||
else l = list.func_148124_c(mouseX, mouseY);
|
||||
if (l < 0) {
|
||||
if (mouseY < (list.bottom + list.top) / 2) {
|
||||
l = 0;
|
||||
} else {
|
||||
l = list.entries.size();
|
||||
}
|
||||
}
|
||||
entriesList.func_148143_b(true);
|
||||
entriesList.entries.remove(index);
|
||||
list.func_148143_b(false);
|
||||
list.entries.add(l, this);
|
||||
this.entriesList = list;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastScroll > 20) {
|
||||
if (mouseY < entriesList.top) {
|
||||
entriesList.scrollBy(-3);
|
||||
lastScroll = now;
|
||||
} else if (mouseY > entriesList.bottom) {
|
||||
entriesList.scrollBy(3);
|
||||
lastScroll = now;
|
||||
}
|
||||
}
|
||||
int l = entriesList.func_148124_c(
|
||||
MathHelper.clamp_int(mouseX, entriesList.left, entriesList.right),
|
||||
MathHelper.clamp_int(mouseY, entriesList.top, entriesList.bottom));
|
||||
if (l < 0 || l == index) return true;
|
||||
entriesList.entries.remove(index);
|
||||
entriesList.entries.add(l, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) {
|
||||
if (btnDrag.mousePressed(mc, x, y)) {
|
||||
isDragging = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String trans(String transKey, String fallback) {
|
||||
String translated = I18n.format(transKey);
|
||||
return translated.equals(transKey) ? fallback : translated;
|
||||
}
|
||||
|
||||
private static class GuiButtonDrag extends GuiButtonExt {
|
||||
|
||||
public GuiButtonDrag(int id, int xPos, int yPos, int width, int height) {
|
||||
super(id, xPos, yPos, width, height, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft mc, int mouseX, int mouseY) {
|
||||
if (this.visible) {
|
||||
this.field_146123_n = mouseX >= this.xPosition && mouseY >= this.yPosition
|
||||
&& mouseX < this.xPosition + this.width
|
||||
&& mouseY < this.yPosition + this.height;
|
||||
int k = this.getHoverState(this.field_146123_n);
|
||||
GuiUtils.drawContinuousTexturedBox(
|
||||
buttonTextures,
|
||||
this.xPosition,
|
||||
this.yPosition,
|
||||
0,
|
||||
46 + k * 20,
|
||||
this.width,
|
||||
this.height,
|
||||
200,
|
||||
20,
|
||||
2,
|
||||
3,
|
||||
2,
|
||||
2,
|
||||
this.zLevel);
|
||||
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/fourwayarrow2.png"));
|
||||
int x = this.xPosition + (this.width - 12) / 2;
|
||||
int y = this.yPosition + (this.height - 12) / 2;
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.addVertexWithUV(x, y + 12, zLevel, 0, 1);
|
||||
tessellator.addVertexWithUV(x + 12, y + 12, zLevel, 1, 1);
|
||||
tessellator.addVertexWithUV(x + 12, y, zLevel, 1, 0);
|
||||
tessellator.addVertexWithUV(x, y, zLevel, 0, 0);
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
|
||||
import cpw.mods.fml.client.config.GuiConfig;
|
||||
import cpw.mods.fml.client.config.GuiConfigEntries;
|
||||
import cpw.mods.fml.client.config.IConfigElement;
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public final class RegistryOrderEntry extends GuiConfigEntries.CategoryEntry {
|
||||
|
||||
public RegistryOrderEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList,
|
||||
IConfigElement<?> configElement) {
|
||||
super(owningScreen, owningEntryList, configElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuiScreen buildChildScreen() {
|
||||
List<IConfigElement<?>> childElements = this.configElement.getChildElements();
|
||||
IConfigElement<?> disabled = null, order = null;
|
||||
for (IConfigElement<?> e : childElements) {
|
||||
if (e.getName().equals("disabled")) disabled = e;
|
||||
else if (e.getName().equals("ordering")) order = e;
|
||||
}
|
||||
if (disabled == null || order == null) {
|
||||
throw new IllegalArgumentException("Did not find expected config entires");
|
||||
}
|
||||
return new GuiEditRegistryConfig(
|
||||
this.owningScreen,
|
||||
disabled,
|
||||
order,
|
||||
configElement.getName(),
|
||||
this.owningScreen.modID,
|
||||
null,
|
||||
owningScreen.allRequireWorldRestart || this.configElement.requiresWorldRestart(),
|
||||
owningScreen.allRequireMcRestart || this.configElement.requiresMcRestart(),
|
||||
this.owningScreen.title,
|
||||
((this.owningScreen.titleLine2 == null ? "" : this.owningScreen.titleLine2) + " > " + this.name));
|
||||
}
|
||||
|
||||
private GuiEditRegistryConfig getChildScreen() {
|
||||
return (GuiEditRegistryConfig) childScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveConfigElement() {
|
||||
GuiEditRegistryConfig c = getChildScreen();
|
||||
if (c != null && c.isInitialized()) return c.saveConfigElements();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChanged() {
|
||||
GuiEditRegistryConfig c = getChildScreen();
|
||||
return c != null && c.isInitialized() && c.isChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault() {
|
||||
GuiEditRegistryConfig c = getChildScreen();
|
||||
return c != null && c.isInitialized() && c.isDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault() {
|
||||
GuiEditRegistryConfig c = getChildScreen();
|
||||
if (c != null && c.isInitialized()) c.setToDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoChanges() {
|
||||
GuiEditRegistryConfig c = getChildScreen();
|
||||
if (c != null && c.isInitialized()) c.undoChanges();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.PlayerEvent;
|
||||
|
||||
public class SortedRegistry<V> implements Iterable<V> {
|
||||
|
||||
static final ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES = new ConcurrentHashMap<>();
|
||||
static {
|
||||
RegistryOrderSyncMessage.Handler.setAllRegistries(ALL_REGISTRIES);
|
||||
}
|
||||
private final NavigableMap<String, V> store = new TreeMap<>();
|
||||
/**
|
||||
* Store stuff in an array for faster iteration access than a red black tree
|
||||
*/
|
||||
private List<V> baked = Collections.emptyList();
|
||||
|
||||
private final Map<UUID, Pair<List<String>, List<String>>> playerOrdering = new HashMap<>();
|
||||
private final Map<UUID, List<V>> playerBaked = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Construct an unsynchronized registry
|
||||
*/
|
||||
public SortedRegistry() {}
|
||||
|
||||
/**
|
||||
* Construct a synchronized registry whose ordering will be synced to the server
|
||||
*
|
||||
* @param name network name
|
||||
*/
|
||||
|
||||
public SortedRegistry(String name) {
|
||||
WeakReference<SortedRegistry<?>> oldValue = ALL_REGISTRIES.put(name, new WeakReference<>(this));
|
||||
if (oldValue != null && oldValue.get() != null) {
|
||||
throw new IllegalArgumentException("duplicate name");
|
||||
}
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
}
|
||||
|
||||
public static Set<String> getRegistryNames() {
|
||||
return ALL_REGISTRIES.keySet();
|
||||
}
|
||||
|
||||
public static SortedRegistry<?> getRegistry(String name) {
|
||||
WeakReference<SortedRegistry<?>> ref = ALL_REGISTRIES.get(name);
|
||||
return ref != null ? ref.get() : null;
|
||||
}
|
||||
|
||||
static void cleanup(MinecraftServer server) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<EntityPlayerMP> players = server.getConfigurationManager().playerEntityList;
|
||||
Set<UUID> onlinePlayerIDs = players.stream().map(EntityPlayerMP::getUniqueID).collect(Collectors.toSet());
|
||||
int removed = 0;
|
||||
for (WeakReference<SortedRegistry<?>> ref : ALL_REGISTRIES.values()) {
|
||||
SortedRegistry<?> registry = ref.get();
|
||||
if (registry == null) continue;
|
||||
for (Iterator<UUID> iterator = registry.playerOrdering.keySet().iterator(); iterator.hasNext();) {
|
||||
UUID uuid = iterator.next();
|
||||
if (!onlinePlayerIDs.contains(uuid)) {
|
||||
iterator.remove();
|
||||
registry.playerBaked.remove(uuid);
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed > 0) {
|
||||
StructureLib.LOGGER.debug("Removed {} registry record for logged out players", removed);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerLogOut(PlayerEvent.PlayerLoggedOutEvent e) {
|
||||
playerOrdering.remove(e.player.getUniqueID());
|
||||
playerBaked.remove(e.player.getUniqueID());
|
||||
}
|
||||
|
||||
public void register(String key, V val) {
|
||||
if (key == null || val == null) throw new NullPointerException();
|
||||
V old = store.putIfAbsent(key, val);
|
||||
if (old != null) {
|
||||
throw new IllegalArgumentException("Duplicate key: " + key);
|
||||
}
|
||||
baked = new ArrayList<>(store.values());
|
||||
for (Map.Entry<UUID, Pair<List<String>, List<String>>> e : playerOrdering.entrySet()) {
|
||||
makePlayerBaked(e.getKey(), e.getValue().getKey(), e.getValue().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void registerOrdering(EntityPlayerMP player, List<String> ordering, List<String> disabled) {
|
||||
registerOrdering(player.getUniqueID(), ordering, disabled);
|
||||
}
|
||||
|
||||
public void registerOrdering(UUID playerID, List<String> ordering, List<String> disabled) {
|
||||
playerOrdering.put(playerID, Pair.of(ordering, disabled));
|
||||
makePlayerBaked(playerID, ordering, disabled);
|
||||
}
|
||||
|
||||
private void makePlayerBaked(UUID player, List<String> ordering, List<String> disabled) {
|
||||
TreeMap<String, V> copy = new TreeMap<>(store);
|
||||
for (String s : disabled) {
|
||||
copy.remove(s);
|
||||
}
|
||||
List<V> b = new ArrayList<>();
|
||||
for (String k : ordering) {
|
||||
V v = copy.remove(k);
|
||||
if (v != null) {
|
||||
b.add(v);
|
||||
}
|
||||
}
|
||||
b.addAll(copy.values());
|
||||
playerBaked.put(player, b);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return store.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return store.isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return store.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(V value) {
|
||||
return store.containsValue(value);
|
||||
}
|
||||
|
||||
public V get(String key) {
|
||||
return store.get(key);
|
||||
}
|
||||
|
||||
public Iterable<V> getPlayerOrdering(@Nullable EntityPlayerMP player) {
|
||||
if (player == null) return baked;
|
||||
List<V> playerPreference = playerBaked.get(player.getUniqueID());
|
||||
if (playerPreference == null) return baked;
|
||||
return playerPreference;
|
||||
}
|
||||
|
||||
public Stream<String> getPlayerOrderingKeys(@Nullable EntityPlayerMP player) {
|
||||
if (player == null) return store.keySet().stream();
|
||||
List<V> playerOrder = playerBaked.get(player.getUniqueID());
|
||||
if (playerOrder == null) return store.keySet().stream();
|
||||
|
||||
// rebuild the order from the baked map
|
||||
Map<V, String> reverseMap = new IdentityHashMap<>();
|
||||
store.forEach((k, v) -> reverseMap.put(v, k));
|
||||
return playerOrder.stream().map(reverseMap::get);
|
||||
}
|
||||
|
||||
Iterable<String> getCurrentOrdering() {
|
||||
return store.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return baked.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super V> action) {
|
||||
baked.forEach(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<V> spliterator() {
|
||||
return baked.spliterator();
|
||||
}
|
||||
}
|
||||
@@ -12,18 +12,22 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.gtnewhorizon.structurelib.block.BlockHint;
|
||||
import com.gtnewhorizon.structurelib.command.CommandConfigureChannels;
|
||||
import com.gtnewhorizon.structurelib.command.CommandRegistryDebug;
|
||||
import com.gtnewhorizon.structurelib.item.ItemBlockHint;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.item.ItemFrontRotationTool;
|
||||
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.util.InventoryUtility;
|
||||
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||
@@ -65,6 +69,7 @@ public class StructureLib {
|
||||
net.registerMessage(UpdateHintParticleMessage.Handler.class, UpdateHintParticleMessage.class, 2, Side.CLIENT);
|
||||
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
|
||||
net.registerMessage(ErrorHintParticleMessage.Handler.class, ErrorHintParticleMessage.class, 4, Side.CLIENT);
|
||||
net.registerMessage(RegistryOrderSyncMessage.Handler.class, RegistryOrderSyncMessage.class, 5, Side.SERVER);
|
||||
|
||||
try {
|
||||
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
|
||||
@@ -109,14 +114,21 @@ public class StructureLib {
|
||||
proxy.preInit(e);
|
||||
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
|
||||
|
||||
InventoryUtility.init();
|
||||
if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) {
|
||||
COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod();
|
||||
}
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void loadComplete(FMLLoadCompleteEvent e) {
|
||||
proxy.loadComplete(e);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void serverStarting(FMLServerStartingEvent e) {
|
||||
e.registerServerCommand(new CommandConfigureChannels());
|
||||
e.registerServerCommand(new CommandRegistryDebug());
|
||||
}
|
||||
|
||||
public static void addClientSideChatMessages(String... messages) {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.gtnewhorizon.structurelib.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.command.WrongUsageException;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
|
||||
import com.gtnewhorizon.structurelib.SortedRegistry;
|
||||
|
||||
public class CommandRegistryDebug extends CommandBase {
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "sl_registry_debug";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandUsage(ICommandSender p_71518_1_) {
|
||||
return "/sl_registry_debug <registry name>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRequiredPermissionLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> addTabCompletionOptions(ICommandSender p_71516_1_, String[] p_71516_2_) {
|
||||
if (p_71516_2_.length == 1) return getListOfStringsMatchingLastWord(
|
||||
p_71516_2_,
|
||||
SortedRegistry.getRegistryNames().toArray(new String[0]));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processCommand(ICommandSender sender, String[] args) {
|
||||
if (args.length != 1) throw new WrongUsageException(getCommandUsage(sender));
|
||||
|
||||
if (!(sender instanceof EntityPlayerMP)) throw new WrongUsageException("must be a player");
|
||||
|
||||
EntityPlayerMP player = (EntityPlayerMP) sender;
|
||||
|
||||
SortedRegistry<?> registry = SortedRegistry.getRegistry(args[0]);
|
||||
if (registry == null) throw new WrongUsageException("registry not found");
|
||||
|
||||
player.addChatMessage(new ChatComponentText("Current order:"));
|
||||
registry.getPlayerOrderingKeys(player).map(ChatComponentText::new).forEach(player::addChatMessage);
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,13 @@ public class ItemConstructableTrigger extends Item {
|
||||
|
||||
@Override
|
||||
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
|
||||
if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null)
|
||||
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||
if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
if (player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
} else {
|
||||
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||
}
|
||||
}
|
||||
return super.onItemRightClick(stack, world, player);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.gtnewhorizon.structurelib.SortedRegistry;
|
||||
|
||||
import cpw.mods.fml.common.network.ByteBufUtils;
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
|
||||
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class RegistryOrderSyncMessage implements IMessage {
|
||||
|
||||
private String name;
|
||||
private List<String> order;
|
||||
private List<String> disabled;
|
||||
|
||||
public RegistryOrderSyncMessage() {}
|
||||
|
||||
public RegistryOrderSyncMessage(String name, List<String> order, List<String> disabled) {
|
||||
this.name = name;
|
||||
this.order = order;
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
name = ByteBufUtils.readUTF8String(buf);
|
||||
int size = ByteBufUtils.readVarInt(buf, 2);
|
||||
order = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
order.add(ByteBufUtils.readUTF8String(buf));
|
||||
}
|
||||
size = ByteBufUtils.readVarInt(buf, 2);
|
||||
disabled = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
disabled.add(ByteBufUtils.readUTF8String(buf));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
ByteBufUtils.writeUTF8String(buf, name);
|
||||
ByteBufUtils.writeVarInt(buf, order.size(), 2);
|
||||
for (String s : order) {
|
||||
ByteBufUtils.writeUTF8String(buf, s);
|
||||
}
|
||||
ByteBufUtils.writeVarInt(buf, disabled.size(), 2);
|
||||
for (String s : disabled) {
|
||||
ByteBufUtils.writeUTF8String(buf, s);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Handler implements IMessageHandler<RegistryOrderSyncMessage, IMessage> {
|
||||
|
||||
private static ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES;
|
||||
|
||||
public static void setAllRegistries(ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> r) {
|
||||
if (ALL_REGISTRIES == null) ALL_REGISTRIES = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(RegistryOrderSyncMessage message, MessageContext ctx) {
|
||||
if (ALL_REGISTRIES == null) return null;
|
||||
WeakReference<SortedRegistry<?>> ref = ALL_REGISTRIES.get(message.name);
|
||||
if (ref == null) return null;
|
||||
SortedRegistry<?> r = ref.get();
|
||||
if (r == null) return null;
|
||||
r.registerOrdering(ctx.getServerHandler().playerEntity, message.order, message.disabled);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,6 +594,9 @@ public class StructureUtility {
|
||||
* Implementation wise, this allows a block only when we don't yet have a tier, or the block at that location has
|
||||
* the same tier as we already have.
|
||||
* <p>
|
||||
* Notice that if your tier extractor function returns null on any (Block, meta) pair, then it means this block is
|
||||
* rejected, since null can never be a valid tier.
|
||||
* <p>
|
||||
* There is yet no TileEntity counterpart of this utility. Feel free to submit a PR to add it.
|
||||
* <p>
|
||||
* For most typical use cases, you will NOT want to return notSet from your tierExtractor. If you do so, we will
|
||||
@@ -637,7 +640,8 @@ public class StructureUtility {
|
||||
* </pre>
|
||||
*
|
||||
* @param tierExtractor a function to extract tier info from a block. This function can return null and will never
|
||||
* be passed a null block or an invalid block meta.
|
||||
* be passed a null block or an invalid block meta. If this function returns null, then the
|
||||
* block would be considered invalid.
|
||||
* @param allKnownTiers A list of all known tiers as of calling. Can be empty or null. No hint will be spawned if
|
||||
* empty or null. Cannot have null elements. First element denotes the most primitive tier.
|
||||
* Last element denotes the most advanced tier. If not all tiers are available at definition
|
||||
|
||||
@@ -33,6 +33,7 @@ import net.minecraft.inventory.InventoryEnderChest;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
|
||||
import com.gtnewhorizon.structurelib.SortedRegistry;
|
||||
import com.gtnewhorizon.structurelib.util.InventoryUtility.ItemStackExtractor.APIType;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||
|
||||
@@ -41,12 +42,13 @@ import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||
*/
|
||||
public class InventoryUtility {
|
||||
|
||||
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>();
|
||||
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>("stackExtractors");
|
||||
private static final List<Predicate<? super EntityPlayerMP>> enableEnder = new CopyOnWriteArrayList<>();
|
||||
/**
|
||||
* The remove() of the Iterable returned must be implemented!
|
||||
*/
|
||||
private static final SortedRegistry<InventoryProvider<?>> inventoryProviders = new SortedRegistry<>();
|
||||
private static final SortedRegistry<InventoryProvider<?>> inventoryProviders = new SortedRegistry<>(
|
||||
"inventoryProviders");
|
||||
|
||||
static {
|
||||
inventoryProviders.register("5000-main-inventory", new InventoryProvider<InventoryIterable<InventoryPlayer>>() {
|
||||
@@ -105,8 +107,8 @@ public class InventoryUtility {
|
||||
registerInventoryProvider(key, newInventoryProvider(extractor));
|
||||
}
|
||||
|
||||
public static Iterator<? extends ItemStackExtractor> getStackExtractors() {
|
||||
return stackExtractors.iterator();
|
||||
public static Iterator<? extends ItemStackExtractor> getStackExtractors(EntityPlayerMP player) {
|
||||
return stackExtractors.getPlayerOrdering(player).iterator();
|
||||
}
|
||||
|
||||
public static <Inv extends IInventory> InventoryProvider<InventoryIterable<Inv>> newInventoryProvider(
|
||||
@@ -168,7 +170,7 @@ public class InventoryUtility {
|
||||
boolean simulate, int count) {
|
||||
ItemStackCounterImpl store = new ItemStackCounterImpl();
|
||||
int sum = 0;
|
||||
for (InventoryProvider<?> provider : inventoryProviders) {
|
||||
for (InventoryProvider<?> provider : inventoryProviders.getPlayerOrdering(player)) {
|
||||
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, null);
|
||||
if (sum >= count) return store.getStore();
|
||||
}
|
||||
@@ -189,7 +191,7 @@ public class InventoryUtility {
|
||||
int count = filter.stackSize;
|
||||
ItemStackPredicate predicate = ItemStackPredicate.from(filter, NBTMode.EXACT);
|
||||
ItemStackCounterImpl store = new ItemStackCounterImpl();
|
||||
for (InventoryProvider<?> provider : inventoryProviders) {
|
||||
for (InventoryProvider<?> provider : inventoryProviders.getPlayerOrdering(player)) {
|
||||
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, filter);
|
||||
if (sum >= count) return sum;
|
||||
}
|
||||
@@ -215,7 +217,7 @@ public class InventoryUtility {
|
||||
* @param predicate item stack filter
|
||||
* @param simulate whether to do removal
|
||||
* @param count let's hope int size is enough...
|
||||
* @param recursive do recursive lookup using {@link #getStackExtractors() stack extractors}
|
||||
* @param recursive do recursive lookup using {@link #getStackExtractors(EntityPlayerMP) stack extractors}
|
||||
* @return amount taken. never negative nor bigger than count...
|
||||
*/
|
||||
public static Map<ItemStack, Integer> takeFromInventory(Iterable<ItemStack> inv, Predicate<ItemStack> predicate,
|
||||
@@ -255,7 +257,7 @@ public class InventoryUtility {
|
||||
if (found == count) return count;
|
||||
}
|
||||
if (!recursive) continue;
|
||||
for (ItemStackExtractor f : stackExtractors) {
|
||||
for (ItemStackExtractor f : stackExtractors.getPlayerOrdering(player)) {
|
||||
boolean end = f.isAPIImplemented(APIType.IS_VALID_SOURCE);
|
||||
if (!f.isValidSource(stack, player)) continue;
|
||||
if (filter != null && f.isAPIImplemented(APIType.EXTRACT_ONE_STACK)) {
|
||||
@@ -271,6 +273,10 @@ public class InventoryUtility {
|
||||
return found;
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// dummy method to init static final fields
|
||||
}
|
||||
|
||||
public interface OptimizedExtractor {
|
||||
|
||||
/**
|
||||
|
||||
@@ -251,5 +251,42 @@ The code for placeBlock() and spawnHint() will be left as an exercise for the re
|
||||
<p>
|
||||
Please refer to {@link com.gtnewhorizon.structurelib.StructureEvent} to see what events are available.
|
||||
</p>
|
||||
<h1>Extending Inventory Utility</h1>
|
||||
{@link com.gtnewhorizon.structurelib.util.InventoryUtility} is how structurelib survival autoplace pulls item for autoplacing.
|
||||
It has two key concepts: inventory providers and stack extractors.
|
||||
<p>
|
||||
Inventory Providers are classes that retrieve an Iterable of ItemStacks for examination, given an EntityPlayerMP as identifier.
|
||||
They are also responsible for marking the relevant item container as dirty, so changes can be commited to disk properly when world save happens.
|
||||
Examples include: main player inventory, vanilla ender chest, baubles inventory. StructureLib comes with main player inventory and vanilla ender chest built in.
|
||||
</p>
|
||||
<p>
|
||||
Stack Extractors are classes that retrieve items from another item.
|
||||
Examples include: Forestry backpacks, ME network over ME Wireless Terminals.
|
||||
</p>
|
||||
<p>
|
||||
Both of these are managed internally by {@link com.gtnewhorizon.structurelib.SortedRegistry}.
|
||||
By default, elements registered will be sorted in lexicographical order in ascending order.
|
||||
However, facilities in StructureLib will allow an end user to tweak these registries as their wish.
|
||||
They can disable, reorder, reenable any element within the registry, either in game, at main menu or with the game stopped,
|
||||
Their changes will be synced to server as well, should they be playing on a dedicated server.
|
||||
</p>
|
||||
<p>
|
||||
This has the following important implications
|
||||
</p>
|
||||
<ul>
|
||||
<li>Not all inventory providers or stack extractors may be called during a survival autoplace session</li>
|
||||
<li>One player could have stack extractor 001 ordered before 002, while the other having a completely reversed order</li>
|
||||
</ul>
|
||||
Therefore, if your stack extractor can somehow correlate with aonther one, make sure it still works if the calling order is inverted.
|
||||
At the very least, inform the player about this ordering constraint.
|
||||
A future StructrueLib refactor to specify this dependency info is possible, but the chance is low.
|
||||
<h2>Localization</h2>
|
||||
Both of these would show up in config GUI of StructureLib. Structurelib uses the following localization keys in that GUI.
|
||||
<ul>
|
||||
<li><code>structurelib.sortedregistry.inventoryproviders.<registry_key></code>: This is the name shown on the button for inventory providers. If this is not provided, then registry_key will be used instead.</li>
|
||||
<li><code>structurelib.sortedregistry.inventoryproviders.<registry_key>.tooltip</code>: This is the tooltip shown when user hover over the button for inventory providers. If this is not provided, then no explicit tooltip will be shown.</li>
|
||||
<li><code>structurelib.sortedregistry.stackextractors.<registry_key></code>: This is the name shown on the button for stack extractors. If this is not provided, then registry_key will be used instead.</li>
|
||||
<li><code>structurelib.sortedregistry.stackextractors.<registry_key>.tooltip</code>: This is the tooltip shown when user hover over the button for stack extractors. If this is not provided, then no explicit tooltip will be shown.</li>
|
||||
</ul>
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -172,3 +172,19 @@ structurelib.config.client.hologram.maxCoexisting=Max Coexisting Holograms
|
||||
structurelib.config.client.hologram.maxCoexisting.tooltip=Number of holograms that can coexist. Usually all hints generated by one right click belongs to one hologram. An attempt will be made to prune old holograms when a new hologram is about to be projected and this limit would be exceeded.
|
||||
structurelib.config.client.hologram.removeColliding=Remove Colliding Holograms
|
||||
structurelib.config.client.hologram.removeColliding.tooltip=Whether an attempt will be made to remove any existing hologram that collides with a new hologram.
|
||||
|
||||
structurelib.config.registries=Sorted Registries
|
||||
structurelib.config.registries.tooltip=Sorted Registry is how StructureLib internally keep track of certain features. Each individual elements may be reordered, disabled and enabled at user's command. Changes will be synced to server if needed.\nThis gui can also be accessed in game by shift right clicking air with Multiblock Structure Hologram Projector.
|
||||
structurelib.config.registries.stackextractors=Stack Extractors
|
||||
structurelib.config.registries.stackextractors.tooltip=Controls which inventory-from-stack functions are enabled during survival autoplace
|
||||
structurelib.config.registries.inventoryproviders=Inventory Providers
|
||||
structurelib.config.registries.inventoryproviders.tooltip=Controls which inventories functions are enabled during survival autoplace
|
||||
|
||||
structurelib.configgui.enabled=Enabled Entry
|
||||
structurelib.configgui.disabled=Disabled Entry
|
||||
structurelib.configgui.drag.tooltip=Drag this button to reorder this element.
|
||||
|
||||
structurelib.sortedregistry.inventoryproviders.5000-main-inventory=Main Player Inventory
|
||||
structurelib.sortedregistry.inventoryproviders.5000-main-inventory.tooltip=36 slots of item in your main inventory. Armor slots excluded.
|
||||
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory=Ender Chest Inventory
|
||||
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory.tooltip=Links to your vanilla ender chest inventory. Require at least one "ender backpack like" item in main inventory to work.
|
||||
@@ -172,3 +172,19 @@ structurelib.config.client.hologram.maxCoexisting=最多可以同时存在的全
|
||||
structurelib.config.client.hologram.maxCoexisting.tooltip=这决定最多可以同时存在多少台机器的全息投影。一般一次使用全息投影仪右键生成的所有机器结构细节被视为一个全息投影。当同时存在多个全息投影,并超过此数量时,再次使用全息投影仪会移除旧有的全息投影。
|
||||
structurelib.config.client.hologram.removeColliding=移除冲突的全息投影
|
||||
structurelib.config.client.hologram.removeColliding.tooltip=当新的全息投影与已存在的全息投影冲突时,开启此选项时会移除已存在的全息投影。
|
||||
|
||||
structurelib.config.registries=注册表
|
||||
structurelib.config.registries.tooltip=StructureLib 使用注册表来管理其部分功能。每个注册表中的每一个元素都可以被单独排序、禁用。需要时,注册表的内容将被同步至服务端。
|
||||
structurelib.config.registries.stackextractors=物品提取器
|
||||
structurelib.config.registries.stackextractors.tooltip=该注册表控制在生存模式自动放置时,如何从物品(如林业背包)提取库存。
|
||||
structurelib.config.registries.inventoryproviders=物品栏定位
|
||||
structurelib.config.registries.inventoryproviders.tooltip=该注册表控制在生存模式自动放置时,可用的物品栏来源。
|
||||
|
||||
structurelib.configgui.enabled=已启用
|
||||
structurelib.configgui.disabled=已禁用
|
||||
structurelib.configgui.drag.tooltip=拖动该按钮以重排。
|
||||
|
||||
structurelib.sortedregistry.inventoryproviders.5000-main-inventory=玩家物品栏
|
||||
structurelib.sortedregistry.inventoryproviders.5000-main-inventory.tooltip=不包括护甲栏以外的36格。
|
||||
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory=末影箱
|
||||
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory.tooltip=当前玩家的末影箱。需要在主物品栏中存在类似于末影背包的东西才会实际生效。
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 367 B |
@@ -0,0 +1 @@
|
||||
public net.minecraft.client.gui.GuiSlot func_148136_c(IIII)V # overlayBackground
|
||||
Reference in new issue
Block a user