Commit e13e19ac4972f7bae1a25a94e104efa7b027527f

Authored by Mumfrey
2 parents 53881326 a4937cc6

Backport all 1.12+ changes to 1.11.2

Showing 25 changed files with 720 additions and 343 deletions
build.gradle
@@ -60,8 +60,8 @@ archivesBaseName = "liteloader" @@ -60,8 +60,8 @@ archivesBaseName = "liteloader"
60 version = buildVersion + (project.isReleaseBuild ? '' : '-' + project.classifier) 60 version = buildVersion + (project.isReleaseBuild ? '' : '-' + project.classifier)
61 61
62 // Minimum version of Java required 62 // Minimum version of Java required
63 -sourceCompatibility = '1.6'  
64 -targetCompatibility = '1.6' 63 +sourceCompatibility = '1.8'
  64 +targetCompatibility = '1.8'
65 65
66 repositories { 66 repositories {
67 mavenLocal() 67 mavenLocal()
@@ -72,7 +72,7 @@ repositories { @@ -72,7 +72,7 @@ repositories {
72 } 72 }
73 73
74 dependencies { 74 dependencies {
75 - compile('org.spongepowered:mixin:0.6.8-SNAPSHOT') { 75 + compile('org.spongepowered:mixin:0.7.1-SNAPSHOT') {
76 exclude module: 'asm-commons' 76 exclude module: 'asm-commons'
77 exclude module: 'asm-tree' 77 exclude module: 'asm-tree'
78 exclude module: 'launchwrapper' 78 exclude module: 'launchwrapper'
@@ -89,11 +89,11 @@ minecraft { @@ -89,11 +89,11 @@ minecraft {
89 89
90 sourceSets { 90 sourceSets {
91 main { 91 main {
92 - refMap = "mixins.liteloader.core.refmap.json" 92 + ext.refMap = "mixins.liteloader.core.refmap.json"
93 } 93 }
94 client { 94 client {
95 compileClasspath += main.compileClasspath + main.output 95 compileClasspath += main.compileClasspath + main.output
96 - refMap = "mixins.liteloader.client.refmap.json" 96 + ext.refMap = "mixins.liteloader.client.refmap.json"
97 } 97 }
98 debug { 98 debug {
99 compileClasspath += client.compileClasspath + client.output 99 compileClasspath += client.compileClasspath + client.output
@@ -123,7 +123,7 @@ javadoc { @@ -123,7 +123,7 @@ javadoc {
123 afterEvaluate { 123 afterEvaluate {
124 logger.lifecycle '=================================================' 124 logger.lifecycle '================================================='
125 logger.lifecycle ' LiteLoader' 125 logger.lifecycle ' LiteLoader'
126 - logger.lifecycle ' Copyright (C) 2011-2016 Adam Mummery-Smith' 126 + logger.lifecycle ' Copyright (C) 2011-2017 Adam Mummery-Smith'
127 logger.lifecycle ' Running in {} mode', (project.isReleaseBuild ? "RELEASE" : "SNAPSHOT") 127 logger.lifecycle ' Running in {} mode', (project.isReleaseBuild ? "RELEASE" : "SNAPSHOT")
128 logger.lifecycle '=================================================' 128 logger.lifecycle '================================================='
129 129
gradle.properties
@@ -7,4 +7,4 @@ organization=LiteLoader @@ -7,4 +7,4 @@ organization=LiteLoader
7 buildType=SNAPSHOT 7 buildType=SNAPSHOT
8 buildVersion=1.11.2 8 buildVersion=1.11.2
9 mcVersion=1.11.2 9 mcVersion=1.11.2
10 -mcMappings=snapshot_20161224  
11 \ No newline at end of file 10 \ No newline at end of file
  11 +mcMappings=snapshot_20161224
src/client/java/com/mumfrey/liteloader/client/LiteLoaderEventBrokerClient.java
@@ -9,24 +9,9 @@ import org.lwjgl.input.Mouse; @@ -9,24 +9,9 @@ import org.lwjgl.input.Mouse;
9 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
10 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
11 11
12 -import com.mumfrey.liteloader.ChatRenderListener;  
13 -import com.mumfrey.liteloader.EntityRenderListener;  
14 -import com.mumfrey.liteloader.FrameBufferListener;  
15 -import com.mumfrey.liteloader.GameLoopListener;  
16 -import com.mumfrey.liteloader.HUDRenderListener;  
17 -import com.mumfrey.liteloader.InitCompleteListener;  
18 -import com.mumfrey.liteloader.OutboundChatFilter;  
19 -import com.mumfrey.liteloader.OutboundChatListener;  
20 -import com.mumfrey.liteloader.PlayerClickListener; 12 +import com.mumfrey.liteloader.*;
21 import com.mumfrey.liteloader.PlayerInteractionListener.MouseButton; 13 import com.mumfrey.liteloader.PlayerInteractionListener.MouseButton;
22 -import com.mumfrey.liteloader.PostRenderListener;  
23 -import com.mumfrey.liteloader.PreRenderListener;  
24 -import com.mumfrey.liteloader.RenderListener;  
25 -import com.mumfrey.liteloader.ScreenshotListener;  
26 -import com.mumfrey.liteloader.Tickable;  
27 -import com.mumfrey.liteloader.ViewportListener;  
28 import com.mumfrey.liteloader.client.overlays.IEntityRenderer; 14 import com.mumfrey.liteloader.client.overlays.IEntityRenderer;
29 -import com.mumfrey.liteloader.client.overlays.IMinecraft;  
30 import com.mumfrey.liteloader.common.LoadingProgress; 15 import com.mumfrey.liteloader.common.LoadingProgress;
31 import com.mumfrey.liteloader.core.InterfaceRegistrationDelegate; 16 import com.mumfrey.liteloader.core.InterfaceRegistrationDelegate;
32 import com.mumfrey.liteloader.core.LiteLoader; 17 import com.mumfrey.liteloader.core.LiteLoader;
@@ -51,7 +36,6 @@ import net.minecraft.client.shader.Framebuffer; @@ -51,7 +36,6 @@ import net.minecraft.client.shader.Framebuffer;
51 import net.minecraft.entity.Entity; 36 import net.minecraft.entity.Entity;
52 import net.minecraft.network.play.client.CPacketChatMessage; 37 import net.minecraft.network.play.client.CPacketChatMessage;
53 import net.minecraft.server.integrated.IntegratedServer; 38 import net.minecraft.server.integrated.IntegratedServer;
54 -import net.minecraft.util.Timer;  
55 import net.minecraft.util.text.ITextComponent; 39 import net.minecraft.util.text.ITextComponent;
56 40
57 public class LiteLoaderEventBrokerClient extends LiteLoaderEventBroker<Minecraft, IntegratedServer> implements IResourceManagerReloadListener 41 public class LiteLoaderEventBrokerClient extends LiteLoaderEventBroker<Minecraft, IntegratedServer> implements IResourceManagerReloadListener
@@ -427,14 +411,9 @@ public class LiteLoaderEventBrokerClient extends LiteLoaderEventBroker&lt;Minecraft @@ -427,14 +411,9 @@ public class LiteLoaderEventBrokerClient extends LiteLoaderEventBroker&lt;Minecraft
427 /** 411 /**
428 * Callback from the tick hook, ticks all tickable mods 412 * Callback from the tick hook, ticks all tickable mods
429 */ 413 */
430 - public void onTick() 414 + public void onTick(boolean clock, float partialTicks)
431 { 415 {
432 this.profiler.endStartSection("litemods"); 416 this.profiler.endStartSection("litemods");
433 -  
434 - Timer minecraftTimer = ((IMinecraft)this.engine.getClient()).getTimer();  
435 - float partialTicks = minecraftTimer.renderPartialTicks;  
436 - boolean clock = minecraftTimer.elapsedTicks > 0;  
437 -  
438 Minecraft minecraft = this.engine.getClient(); 417 Minecraft minecraft = this.engine.getClient();
439 418
440 // Flag indicates whether we are in game at the moment 419 // Flag indicates whether we are in game at the moment
@@ -548,7 +527,7 @@ public class LiteLoaderEventBrokerClient extends LiteLoaderEventBroker&lt;Minecraft @@ -548,7 +527,7 @@ public class LiteLoaderEventBrokerClient extends LiteLoaderEventBroker&lt;Minecraft
548 } 527 }
549 528
550 /** 529 /**
551 - * @param e 530 + * @param ci
552 * @param name 531 * @param name
553 * @param width 532 * @param width
554 * @param height 533 * @param height
src/client/java/com/mumfrey/liteloader/client/LiteLoaderPanelManager.java
@@ -9,6 +9,7 @@ import org.lwjgl.input.Keyboard; @@ -9,6 +9,7 @@ import org.lwjgl.input.Keyboard;
9 9
10 import com.mumfrey.liteloader.client.gui.GuiLiteLoaderPanel; 10 import com.mumfrey.liteloader.client.gui.GuiLiteLoaderPanel;
11 import com.mumfrey.liteloader.common.GameEngine; 11 import com.mumfrey.liteloader.common.GameEngine;
  12 +import com.mumfrey.liteloader.core.LiteLoader;
12 import com.mumfrey.liteloader.core.LiteLoaderMods; 13 import com.mumfrey.liteloader.core.LiteLoaderMods;
13 import com.mumfrey.liteloader.core.LiteLoaderUpdateSite; 14 import com.mumfrey.liteloader.core.LiteLoaderUpdateSite;
14 import com.mumfrey.liteloader.core.LiteLoaderVersion; 15 import com.mumfrey.liteloader.core.LiteLoaderVersion;
@@ -32,6 +33,13 @@ import net.minecraft.client.resources.I18n; @@ -32,6 +33,13 @@ import net.minecraft.client.resources.I18n;
32 */ 33 */
33 public class LiteLoaderPanelManager implements PanelManager<GuiScreen> 34 public class LiteLoaderPanelManager implements PanelManager<GuiScreen>
34 { 35 {
  36 + /**
  37 + * Number of launches required before an update check is forced when the
  38 + * "force update check" option is enabled. For snapshot versions this is
  39 + * ignored and an update check is always performed.
  40 + */
  41 + private static final int UPDATE_CHECK_INTERVAL = 10;
  42 +
35 private final LoaderEnvironment environment; 43 private final LoaderEnvironment environment;
36 44
37 /** 45 /**
@@ -87,14 +95,14 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt; @@ -87,14 +95,14 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt;
87 this.displayModInfoScreenTab = this.properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_MOD_INFO_SCREEN, true); 95 this.displayModInfoScreenTab = this.properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_MOD_INFO_SCREEN, true);
88 this.tabAlwaysExpanded = this.properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_NO_HIDE_TAB, false); 96 this.tabAlwaysExpanded = this.properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_NO_HIDE_TAB, false);
89 97
90 - if (this.properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_FORCE_UPDATE, false)) 98 + if (this.shouldCheckForUpdates())
91 { 99 {
92 int updateCheckInterval = this.properties.getIntegerProperty(LoaderProperties.OPTION_UPDATE_CHECK_INTR) + 1; 100 int updateCheckInterval = this.properties.getIntegerProperty(LoaderProperties.OPTION_UPDATE_CHECK_INTR) + 1;
93 - LiteLoaderLogger.debug("Force update is TRUE, updateCheckInterval = %d", updateCheckInterval); 101 + LiteLoaderLogger.debug("Regular update check enabled, updateCheckInterval = %d", updateCheckInterval);
94 102
95 - if (updateCheckInterval > 10) 103 + if (LiteLoader.isSnapshot() || updateCheckInterval > LiteLoaderPanelManager.UPDATE_CHECK_INTERVAL)
96 { 104 {
97 - LiteLoaderLogger.debug("Forcing update check!"); 105 + LiteLoaderLogger.debug("Checking for updates...");
98 this.checkForUpdate = true; 106 this.checkForUpdate = true;
99 updateCheckInterval = 0; 107 updateCheckInterval = 0;
100 } 108 }
@@ -104,6 +112,16 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt; @@ -104,6 +112,16 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt;
104 } 112 }
105 } 113 }
106 114
  115 + private boolean shouldCheckForUpdates()
  116 + {
  117 + if (LiteLoader.isSnapshot() && this.properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_CHECK_SNAPSHOTS, true))
  118 + {
  119 + return true;
  120 + }
  121 +
  122 + return this.properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_FORCE_UPDATE, false);
  123 + }
  124 +
107 @Override 125 @Override
108 public void init(LiteLoaderMods mods, ConfigManager configManager) 126 public void init(LiteLoaderMods mods, ConfigManager configManager)
109 { 127 {
@@ -141,7 +159,8 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt; @@ -141,7 +159,8 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt;
141 this.checkForUpdate = false; 159 this.checkForUpdate = false;
142 if (updateSite.isCheckSucceess() && updateSite.isUpdateAvailable()) 160 if (updateSite.isCheckSucceess() && updateSite.isUpdateAvailable())
143 { 161 {
144 - this.setNotification(I18n.format("gui.notifications.updateavailable")); 162 + this.setNotification(I18n.format("gui.notifications." + (LiteLoader.isSnapshot() ? "newsnapshotavailable" : "updateavailable"),
  163 + updateSite.getAvailableVersion(), updateSite.getAvailableVersionDate()));
145 } 164 }
146 } 165 }
147 } 166 }
@@ -251,6 +270,19 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt; @@ -251,6 +270,19 @@ public class LiteLoaderPanelManager implements PanelManager&lt;GuiScreen&gt;
251 { 270 {
252 return this.properties.getBooleanProperty(LoaderProperties.OPTION_FORCE_UPDATE); 271 return this.properties.getBooleanProperty(LoaderProperties.OPTION_FORCE_UPDATE);
253 } 272 }
  273 +
  274 + @Override
  275 + public void setCheckForSnapshotsEnabled(boolean checkForSnapshots)
  276 + {
  277 + this.properties.setBooleanProperty(LoaderProperties.OPTION_CHECK_SNAPSHOTS, checkForSnapshots);
  278 + this.properties.writeProperties();
  279 + }
  280 +
  281 + @Override
  282 + public boolean isCheckForSnapshotsEnabled()
  283 + {
  284 + return this.properties.getBooleanProperty(LoaderProperties.OPTION_CHECK_SNAPSHOTS);
  285 + }
254 286
255 /** 287 /**
256 * Display the liteloader panel over the specified GUI 288 * Display the liteloader panel over the specified GUI
src/client/java/com/mumfrey/liteloader/client/api/LiteLoaderCoreAPIClient.java
@@ -42,7 +42,6 @@ public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI @@ -42,7 +42,6 @@ public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI
42 42
43 private static final String[] requiredDownstreamTransformers = { 43 private static final String[] requiredDownstreamTransformers = {
44 LiteLoaderCoreAPI.PKG_LITELOADER_COMMON + ".transformers.LiteLoaderPacketTransformer", 44 LiteLoaderCoreAPI.PKG_LITELOADER_COMMON + ".transformers.LiteLoaderPacketTransformer",
45 - LiteLoaderCoreAPIClient.PKG_LITELOADER_CLIENT + ".transformers.MinecraftTransformer",  
46 LiteLoaderCoreAPI.PKG_LITELOADER + ".transformers.event.json.ModEventInjectionTransformer" 45 LiteLoaderCoreAPI.PKG_LITELOADER + ".transformers.event.json.ModEventInjectionTransformer"
47 }; 46 };
48 47
src/client/java/com/mumfrey/liteloader/client/gui/GuiLiteLoaderPanel.java
@@ -177,11 +177,10 @@ public class GuiLiteLoaderPanel extends GuiScreen @@ -177,11 +177,10 @@ public class GuiLiteLoaderPanel extends GuiScreen
177 this.startupErrorCount = mods.getStartupErrorCount(); 177 this.startupErrorCount = mods.getStartupErrorCount();
178 this.criticalErrorCount = mods.getCriticalErrorCount(); 178 this.criticalErrorCount = mods.getCriticalErrorCount();
179 179
180 - String branding = LiteLoader.getBranding();  
181 - if (branding != null && branding.contains("SNAPSHOT")) 180 + this.isSnapshot = LiteLoader.isSnapshot();
  181 + if (this.isSnapshot)
182 { 182 {
183 - this.isSnapshot = true;  
184 - this.versionText = "\247c" + branding; 183 + this.versionText = "\247c" + LiteLoader.getBranding();
185 } 184 }
186 } 185 }
187 186
@@ -524,6 +523,7 @@ public class GuiLiteLoaderPanel extends GuiScreen @@ -524,6 +523,7 @@ public class GuiLiteLoaderPanel extends GuiScreen
524 523
525 if (annoyingTip) 524 if (annoyingTip)
526 { 525 {
  526 + GuiLiteLoaderPanel.displayErrorToolTip = false;
527 this.drawNotificationTooltip(mouseX, mouseY - 13); 527 this.drawNotificationTooltip(mouseX, mouseY - 13);
528 } 528 }
529 } 529 }
@@ -542,8 +542,11 @@ public class GuiLiteLoaderPanel extends GuiScreen @@ -542,8 +542,11 @@ public class GuiLiteLoaderPanel extends GuiScreen
542 } 542 }
543 else if (this.notification != null) 543 else if (this.notification != null)
544 { 544 {
545 - GuiLiteLoaderPanel.drawTooltip(this.fontRendererObj, this.notification, left, top, this.width, this.height,  
546 - GuiLiteLoaderPanel.NOTIFICATION_TOOLTIP_FOREGROUND, GuiLiteLoaderPanel.NOTIFICATION_TOOLTIP_BACKGROUND); 545 + boolean isCritical = this.notification.startsWith("!!");
  546 + String text = isCritical ? this.notification.substring(2) : this.notification;
  547 + int bgColour = isCritical ? GuiLiteLoaderPanel.ERROR_TOOLTIP_BACKGROUND : GuiLiteLoaderPanel.NOTIFICATION_TOOLTIP_BACKGROUND;
  548 + GuiLiteLoaderPanel.drawTooltip(this.fontRendererObj, text, left, top, this.width, this.height,
  549 + GuiLiteLoaderPanel.NOTIFICATION_TOOLTIP_FOREGROUND, bgColour);
547 } 550 }
548 } 551 }
549 552
@@ -739,21 +742,32 @@ public class GuiLiteLoaderPanel extends GuiScreen @@ -739,21 +742,32 @@ public class GuiLiteLoaderPanel extends GuiScreen
739 * 742 *
740 * @param fontRenderer 743 * @param fontRenderer
741 * @param tooltipText 744 * @param tooltipText
742 - * @param mouseX  
743 - * @param mouseY 745 + * @param left
  746 + * @param top
744 * @param screenWidth 747 * @param screenWidth
745 * @param screenHeight 748 * @param screenHeight
746 * @param colour 749 * @param colour
747 * @param backgroundColour 750 * @param backgroundColour
748 */ 751 */
749 - public static void drawTooltip(FontRenderer fontRenderer, String tooltipText, int mouseX, int mouseY, int screenWidth, int screenHeight, 752 + public static void drawTooltip(FontRenderer fontRenderer, String text, int left, int top, int screenWidth, int screenHeight,
750 int colour, int backgroundColour) 753 int colour, int backgroundColour)
751 { 754 {
752 - int textSize = fontRenderer.getStringWidth(tooltipText);  
753 - mouseX = Math.max(0, Math.min(screenWidth - 4, mouseX - 4));  
754 - mouseY = Math.max(0, Math.min(screenHeight - 16, mouseY));  
755 - drawRect(mouseX - textSize - 2, mouseY, mouseX + 2, mouseY + 12, backgroundColour);  
756 - fontRenderer.drawStringWithShadow(tooltipText, mouseX - textSize, mouseY + 2, colour); 755 + String[] lines = text.trim().split("\\r?\\n");
  756 + int textWidth = 0;
  757 + int textHeight = 9 * lines.length;
  758 + for (String line : lines)
  759 + {
  760 + textWidth = Math.max(textWidth, fontRenderer.getStringWidth(line));
  761 + top -= 9;
  762 + }
  763 +
  764 + left = Math.max(0, Math.min(screenWidth - 4, left - 4));
  765 + top = Math.max(0, Math.min(screenHeight - 16, top + 9));
  766 + drawRect(left - textWidth - 2, top, left + 2, top + textHeight + 2, backgroundColour);
  767 + for (String line : lines)
  768 + {
  769 + fontRenderer.drawStringWithShadow(line, left - textWidth, (top += 9) - 7, colour);
  770 + }
757 } 771 }
758 772
759 773
src/client/java/com/mumfrey/liteloader/client/gui/GuiPanelError.java
@@ -12,10 +12,12 @@ import java.util.List; @@ -12,10 +12,12 @@ import java.util.List;
12 12
13 import org.lwjgl.input.Keyboard; 13 import org.lwjgl.input.Keyboard;
14 14
  15 +import com.google.common.base.Joiner;
15 import com.mumfrey.liteloader.core.ModInfo; 16 import com.mumfrey.liteloader.core.ModInfo;
16 17
17 import net.minecraft.client.Minecraft; 18 import net.minecraft.client.Minecraft;
18 import net.minecraft.client.gui.GuiButton; 19 import net.minecraft.client.gui.GuiButton;
  20 +import net.minecraft.client.gui.GuiScreen;
19 import net.minecraft.client.resources.I18n; 21 import net.minecraft.client.resources.I18n;
20 22
21 public class GuiPanelError extends GuiPanel implements ScrollPanelContent 23 public class GuiPanelError extends GuiPanel implements ScrollPanelContent
@@ -98,6 +100,7 @@ public class GuiPanelError extends GuiPanel implements ScrollPanelContent @@ -98,6 +100,7 @@ public class GuiPanelError extends GuiPanel implements ScrollPanelContent
98 100
99 this.scrollPane.setSizeAndPosition(MARGIN, TOP, this.width - (MARGIN * 2), this.height - TOP - BOTTOM); 101 this.scrollPane.setSizeAndPosition(MARGIN, TOP, this.width - (MARGIN * 2), this.height - TOP - BOTTOM);
100 this.controls.add(new GuiButton(0, this.width - 59 - MARGIN, this.height - BOTTOM + 9, 60, 20, I18n.format("gui.done"))); 102 this.controls.add(new GuiButton(0, this.width - 59 - MARGIN, this.height - BOTTOM + 9, 60, 20, I18n.format("gui.done")));
  103 + this.controls.add(new GuiButton(1, this.width - 204 - MARGIN, this.height - BOTTOM + 9, 140, 20, I18n.format("gui.error.copytoclipboard")));
101 } 104 }
102 105
103 @Override 106 @Override
@@ -131,7 +134,10 @@ public class GuiPanelError extends GuiPanel implements ScrollPanelContent @@ -131,7 +134,10 @@ public class GuiPanelError extends GuiPanel implements ScrollPanelContent
131 @Override 134 @Override
132 void keyPressed(char keyChar, int keyCode) 135 void keyPressed(char keyChar, int keyCode)
133 { 136 {
134 - if (keyCode == Keyboard.KEY_ESCAPE) this.close(); 137 + if (keyCode == Keyboard.KEY_ESCAPE)
  138 + {
  139 + this.close();
  140 + }
135 } 141 }
136 142
137 @Override 143 @Override
@@ -161,6 +167,14 @@ public class GuiPanelError extends GuiPanel implements ScrollPanelContent @@ -161,6 +167,14 @@ public class GuiPanelError extends GuiPanel implements ScrollPanelContent
161 @Override 167 @Override
162 void actionPerformed(GuiButton control) 168 void actionPerformed(GuiButton control)
163 { 169 {
164 - if (control.id == 0) this.close(); 170 + if (control.id == 0)
  171 + {
  172 + this.close();
  173 + }
  174 +
  175 + if (control.id == 1)
  176 + {
  177 + GuiScreen.setClipboardString(Joiner.on('\n').join(this.scrollPaneContent));
  178 + }
165 } 179 }
166 } 180 }
src/client/java/com/mumfrey/liteloader/client/gui/GuiPanelSettings.java
@@ -17,25 +17,29 @@ import net.minecraft.client.resources.I18n; @@ -17,25 +17,29 @@ import net.minecraft.client.resources.I18n;
17 17
18 class GuiPanelSettings extends GuiPanel 18 class GuiPanelSettings extends GuiPanel
19 { 19 {
20 - private GuiLiteLoaderPanel parentScreen; 20 + private final GuiLiteLoaderPanel parentScreen;
  21 +
  22 + private final boolean isSnapshot;
21 23
22 - private GuiCheckbox chkShowTab, chkNoHide, chkForceUpdate; 24 + private GuiCheckbox chkShowTab, chkNoHide, chkForceUpdate, chkCheckForSnapshots;
23 25
24 private boolean hide; 26 private boolean hide;
25 27
26 private String[] helpText = new String[5]; 28 private String[] helpText = new String[5];
27 - 29 +
28 GuiPanelSettings(GuiLiteLoaderPanel parentScreen, Minecraft minecraft) 30 GuiPanelSettings(GuiLiteLoaderPanel parentScreen, Minecraft minecraft)
29 { 31 {
30 super(minecraft); 32 super(minecraft);
31 33
32 this.parentScreen = parentScreen; 34 this.parentScreen = parentScreen;
  35 + this.isSnapshot = LiteLoader.isSnapshot();
33 36
  37 + String helpKey = this.isSnapshot ? "checkforsnapshots" : "forceupdate";
34 this.helpText[0] = I18n.format("gui.settings.showtab.help1"); 38 this.helpText[0] = I18n.format("gui.settings.showtab.help1");
35 this.helpText[1] = I18n.format("gui.settings.showtab.help2"); 39 this.helpText[1] = I18n.format("gui.settings.showtab.help2");
36 this.helpText[2] = I18n.format("gui.settings.notabhide.help1"); 40 this.helpText[2] = I18n.format("gui.settings.notabhide.help1");
37 - this.helpText[3] = I18n.format("gui.settings.forceupdate.help1");  
38 - this.helpText[4] = I18n.format("gui.settings.forceupdate.help2"); 41 + this.helpText[3] = I18n.format("gui.settings." + helpKey + ".help1");
  42 + this.helpText[4] = I18n.format("gui.settings." + helpKey + ".help2");
39 } 43 }
40 44
41 @Override 45 @Override
@@ -61,6 +65,10 @@ class GuiPanelSettings extends GuiPanel @@ -61,6 +65,10 @@ class GuiPanelSettings extends GuiPanel
61 this.controls.add(this.chkShowTab = new GuiCheckbox(0, 34, 90, I18n.format("gui.settings.showtab.label"))); 65 this.controls.add(this.chkShowTab = new GuiCheckbox(0, 34, 90, I18n.format("gui.settings.showtab.label")));
62 this.controls.add(this.chkNoHide = new GuiCheckbox(1, 34, 128, I18n.format("gui.settings.notabhide.label"))); 66 this.controls.add(this.chkNoHide = new GuiCheckbox(1, 34, 128, I18n.format("gui.settings.notabhide.label")));
63 this.controls.add(this.chkForceUpdate = new GuiCheckbox(2, 34, 158, I18n.format("gui.settings.forceupdate.label"))); 67 this.controls.add(this.chkForceUpdate = new GuiCheckbox(2, 34, 158, I18n.format("gui.settings.forceupdate.label")));
  68 + this.controls.add(this.chkCheckForSnapshots = new GuiCheckbox(2, 34, 158, I18n.format("gui.settings.checkforsnapshots.label")));
  69 +
  70 + this.chkForceUpdate.visible = !this.isSnapshot;
  71 + this.chkCheckForSnapshots.visible = this.isSnapshot;
64 72
65 this.updateCheckBoxes(); 73 this.updateCheckBoxes();
66 } 74 }
@@ -72,6 +80,7 @@ class GuiPanelSettings extends GuiPanel @@ -72,6 +80,7 @@ class GuiPanelSettings extends GuiPanel
72 this.chkShowTab.checked = panelManager.isTabVisible(); 80 this.chkShowTab.checked = panelManager.isTabVisible();
73 this.chkNoHide.checked = panelManager.isTabAlwaysExpanded(); 81 this.chkNoHide.checked = panelManager.isTabAlwaysExpanded();
74 this.chkForceUpdate.checked = panelManager.isForceUpdateEnabled(); 82 this.chkForceUpdate.checked = panelManager.isForceUpdateEnabled();
  83 + this.chkCheckForSnapshots.checked = panelManager.isCheckForSnapshotsEnabled();
75 } 84 }
76 85
77 private void updateSettings() 86 private void updateSettings()
@@ -81,6 +90,7 @@ class GuiPanelSettings extends GuiPanel @@ -81,6 +90,7 @@ class GuiPanelSettings extends GuiPanel
81 panelManager.setTabVisible(this.chkShowTab.checked); 90 panelManager.setTabVisible(this.chkShowTab.checked);
82 panelManager.setTabAlwaysExpanded(this.chkNoHide.checked); 91 panelManager.setTabAlwaysExpanded(this.chkNoHide.checked);
83 panelManager.setForceUpdateEnabled(this.chkForceUpdate.checked); 92 panelManager.setForceUpdateEnabled(this.chkForceUpdate.checked);
  93 + panelManager.setCheckForSnapshotsEnabled(this.chkCheckForSnapshots.checked);
84 } 94 }
85 95
86 @Override 96 @Override
src/client/java/com/mumfrey/liteloader/client/gui/modlist/ModList.java
@@ -15,6 +15,7 @@ import org.lwjgl.input.Keyboard; @@ -15,6 +15,7 @@ import org.lwjgl.input.Keyboard;
15 import com.mumfrey.liteloader.LiteMod; 15 import com.mumfrey.liteloader.LiteMod;
16 import com.mumfrey.liteloader.api.ModInfoDecorator; 16 import com.mumfrey.liteloader.api.ModInfoDecorator;
17 import com.mumfrey.liteloader.client.gui.GuiLiteLoaderPanel; 17 import com.mumfrey.liteloader.client.gui.GuiLiteLoaderPanel;
  18 +import com.mumfrey.liteloader.core.EnabledModsList.Enabled;
18 import com.mumfrey.liteloader.core.LiteLoaderMods; 19 import com.mumfrey.liteloader.core.LiteLoaderMods;
19 import com.mumfrey.liteloader.core.ModInfo; 20 import com.mumfrey.liteloader.core.ModInfo;
20 import com.mumfrey.liteloader.interfaces.Loadable; 21 import com.mumfrey.liteloader.interfaces.Loadable;
@@ -75,9 +76,12 @@ public class ModList @@ -75,9 +76,12 @@ public class ModList
75 // Disabled mods 76 // Disabled mods
76 for (ModInfo<?> disabledMod : mods.getDisabledMods()) 77 for (ModInfo<?> disabledMod : mods.getDisabledMods())
77 { 78 {
78 - ModListEntry modListEntry = new ModListEntry(this, mods, environment, minecraft.fontRendererObj, brandColour, decorators, disabledMod); 79 + if (environment.getEnabledModsList().getEnabled(environment.getProfile(), disabledMod.getIdentifier()) != Enabled.FILTERED)
  80 + {
  81 + ModListEntry modListEntry = new ModListEntry(this, mods, environment, minecraft.fontRendererObj, brandColour, decorators, disabledMod);
79 sortedMods.put(modListEntry.getKey(), modListEntry); 82 sortedMods.put(modListEntry.getKey(), modListEntry);
80 } 83 }
  84 + }
81 85
82 // Show bad containers if no other containers are found, should help users realise they have the wrong mod version! 86 // Show bad containers if no other containers are found, should help users realise they have the wrong mod version!
83 if (sortedMods.size() == 0) 87 if (sortedMods.size() == 0)
src/client/java/com/mumfrey/liteloader/client/mixin/MixinMinecraft.java
@@ -19,7 +19,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -19,7 +19,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
19 import com.mumfrey.liteloader.PlayerInteractionListener.MouseButton; 19 import com.mumfrey.liteloader.PlayerInteractionListener.MouseButton;
20 import com.mumfrey.liteloader.client.LiteLoaderEventBrokerClient; 20 import com.mumfrey.liteloader.client.LiteLoaderEventBrokerClient;
21 import com.mumfrey.liteloader.client.ducks.IFramebuffer; 21 import com.mumfrey.liteloader.client.ducks.IFramebuffer;
  22 +import com.mumfrey.liteloader.client.gui.startup.LoadingBar;
22 import com.mumfrey.liteloader.client.overlays.IMinecraft; 23 import com.mumfrey.liteloader.client.overlays.IMinecraft;
  24 +import com.mumfrey.liteloader.launch.LiteLoaderTweaker;
23 25
24 import net.minecraft.client.Minecraft; 26 import net.minecraft.client.Minecraft;
25 import net.minecraft.client.renderer.OpenGlHelper; 27 import net.minecraft.client.renderer.OpenGlHelper;
@@ -35,6 +37,7 @@ public abstract class MixinMinecraft implements IMinecraft @@ -35,6 +37,7 @@ public abstract class MixinMinecraft implements IMinecraft
35 @Shadow @Final private List<IResourcePack> defaultResourcePacks; 37 @Shadow @Final private List<IResourcePack> defaultResourcePacks;
36 @Shadow private String serverName; 38 @Shadow private String serverName;
37 @Shadow private int serverPort; 39 @Shadow private int serverPort;
  40 + @Shadow private boolean isGamePaused;
38 41
39 @Shadow abstract void resize(int width, int height); 42 @Shadow abstract void resize(int width, int height);
40 @Shadow private void clickMouse() {} 43 @Shadow private void clickMouse() {}
@@ -43,6 +46,25 @@ public abstract class MixinMinecraft implements IMinecraft @@ -43,6 +46,25 @@ public abstract class MixinMinecraft implements IMinecraft
43 46
44 private LiteLoaderEventBrokerClient broker; 47 private LiteLoaderEventBrokerClient broker;
45 48
  49 + @Inject(method = "init()V", at = @At(value = "NEW", target = "net/minecraft/client/renderer/EntityRenderer"))
  50 + private void init(CallbackInfo ci)
  51 + {
  52 + LiteLoaderTweaker.init();
  53 + LiteLoaderTweaker.postInit();
  54 + }
  55 +
  56 + @Inject(method = "init()V", at = @At(value = "NEW", target = "net/minecraft/client/renderer/texture/TextureMap"))
  57 + private void initTextures(CallbackInfo ci)
  58 + {
  59 + LoadingBar.initTextures();
  60 + }
  61 +
  62 + @Inject(method = "init()V", at = @At("INVOKE"))
  63 + private void progress(CallbackInfo ci)
  64 + {
  65 + LoadingBar.incrementProgress();
  66 + }
  67 +
46 @Inject(method = "init()V", at = @At("RETURN")) 68 @Inject(method = "init()V", at = @At("RETURN"))
47 private void onStartupComplete(CallbackInfo ci) 69 private void onStartupComplete(CallbackInfo ci)
48 { 70 {
@@ -79,7 +101,9 @@ public abstract class MixinMinecraft implements IMinecraft @@ -79,7 +101,9 @@ public abstract class MixinMinecraft implements IMinecraft
79 )) 101 ))
80 private void onTick(CallbackInfo ci) 102 private void onTick(CallbackInfo ci)
81 { 103 {
82 - this.broker.onTick(); 104 + boolean clock = this.timer.elapsedTicks > 0;
  105 + float partialTicks = this.timer.renderPartialTicks;
  106 + this.broker.onTick(clock, partialTicks);
83 } 107 }
84 108
85 @Redirect(method = "runGameLoop()V", at = @At( 109 @Redirect(method = "runGameLoop()V", at = @At(
src/client/java/com/mumfrey/liteloader/client/transformers/MinecraftTransformer.java deleted 100644 → 0
1 -/*  
2 - * This file is part of LiteLoader.  
3 - * Copyright (C) 2012-16 Adam Mummery-Smith  
4 - * All Rights Reserved.  
5 - */  
6 -package com.mumfrey.liteloader.client.transformers;  
7 -  
8 -import java.util.Iterator;  
9 -  
10 -import org.objectweb.asm.Opcodes;  
11 -import org.objectweb.asm.tree.AbstractInsnNode;  
12 -import org.objectweb.asm.tree.ClassNode;  
13 -import org.objectweb.asm.tree.InsnList;  
14 -import org.objectweb.asm.tree.LdcInsnNode;  
15 -import org.objectweb.asm.tree.MethodInsnNode;  
16 -import org.objectweb.asm.tree.MethodNode;  
17 -import org.objectweb.asm.tree.TypeInsnNode;  
18 -  
19 -import com.mumfrey.liteloader.core.runtime.Obf;  
20 -import com.mumfrey.liteloader.launch.LiteLoaderTweaker;  
21 -import com.mumfrey.liteloader.transformers.ClassTransformer;  
22 -import com.mumfrey.liteloader.util.log.LiteLoaderLogger;  
23 -  
24 -public class MinecraftTransformer extends ClassTransformer  
25 -{  
26 - private static final String TWEAKCLASS = LiteLoaderTweaker.class.getName().replace('.', '/');  
27 -  
28 - @Override  
29 - public byte[] transform(String name, String transformedName, byte[] basicClass)  
30 - {  
31 - if ((Obf.Minecraft.name.equals(transformedName) || Obf.Minecraft.obf.equals(transformedName)))  
32 - {  
33 - ClassNode classNode = this.readClass(basicClass, true);  
34 - for (MethodNode method : classNode.methods)  
35 - {  
36 - if (Obf.startGame.obf.equals(method.name) || Obf.startGame.srg.equals(method.name) || Obf.startGame.name.equals(method.name))  
37 - {  
38 - this.transformStartGame(method);  
39 - }  
40 - }  
41 - return this.writeClass(classNode);  
42 - }  
43 - return basicClass;  
44 - }  
45 -  
46 -  
47 - private void transformStartGame(MethodNode method)  
48 - {  
49 - InsnList insns = new InsnList();  
50 -  
51 - boolean loadingBarEnabled = LiteLoaderTweaker.loadingBarEnabled();  
52 - boolean found = false;  
53 -  
54 - Iterator<AbstractInsnNode> iter = method.instructions.iterator();  
55 - while (iter.hasNext())  
56 - {  
57 - AbstractInsnNode insn = iter.next();  
58 - if (loadingBarEnabled && insn instanceof MethodInsnNode)  
59 - {  
60 - insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Obf.LoadingBar.ref, "incrementProgress", "()V", false));  
61 - }  
62 -  
63 - insns.add(insn);  
64 -  
65 - if (insn instanceof TypeInsnNode && insn.getOpcode() == Opcodes.NEW && insns.getLast() != null)  
66 - {  
67 - TypeInsnNode typeNode = (TypeInsnNode)insn;  
68 - if (!found && (Obf.EntityRenderer.obf.equals(typeNode.desc) || Obf.EntityRenderer.ref.equals(typeNode.desc)))  
69 - {  
70 - LiteLoaderLogger.info("MinecraftTransformer found INIT injection point, this is good.");  
71 - found = true;  
72 -  
73 - insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, MinecraftTransformer.TWEAKCLASS, Obf.init.name, "()V", false));  
74 - insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, MinecraftTransformer.TWEAKCLASS, Obf.postInit.name, "()V", false));  
75 - }  
76 - }  
77 -  
78 - if (loadingBarEnabled && insn instanceof LdcInsnNode)  
79 - {  
80 - LdcInsnNode ldcInsn = (LdcInsnNode)insn;  
81 - if ("textures/blocks".equals(ldcInsn.cst))  
82 - {  
83 - insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Obf.LoadingBar.ref, "initTextures", "()V", false));  
84 - }  
85 - }  
86 - }  
87 -  
88 - method.instructions = insns;  
89 -  
90 - if (!found) LiteLoaderLogger.severe("MinecraftTransformer failed to find INIT injection point, the game will probably crash pretty soon.");  
91 - }  
92 -}  
src/client/java/com/mumfrey/liteloader/modconfig/AbstractConfigPanel.java
@@ -7,15 +7,21 @@ package com.mumfrey.liteloader.modconfig; @@ -7,15 +7,21 @@ package com.mumfrey.liteloader.modconfig;
7 7
8 import java.util.ArrayList; 8 import java.util.ArrayList;
9 import java.util.List; 9 import java.util.List;
  10 +import java.util.regex.Pattern;
10 11
11 import org.lwjgl.input.Keyboard; 12 import org.lwjgl.input.Keyboard;
12 13
  14 +import com.mumfrey.liteloader.client.gui.GuiLiteLoaderPanel;
13 import com.mumfrey.liteloader.client.mixin.IGuiButton; 15 import com.mumfrey.liteloader.client.mixin.IGuiButton;
14 16
15 import net.minecraft.client.Minecraft; 17 import net.minecraft.client.Minecraft;
  18 +import net.minecraft.client.gui.FontRenderer;
16 import net.minecraft.client.gui.GuiButton; 19 import net.minecraft.client.gui.GuiButton;
17 import net.minecraft.client.gui.GuiLabel; 20 import net.minecraft.client.gui.GuiLabel;
18 import net.minecraft.client.gui.GuiScreen; 21 import net.minecraft.client.gui.GuiScreen;
  22 +import net.minecraft.client.gui.GuiTextField;
  23 +import net.minecraft.client.renderer.RenderHelper;
  24 +import net.minecraft.client.resources.I18n;
19 25
20 /** 26 /**
21 * A general-purpose base class for mod config panels which implements a lot of 27 * A general-purpose base class for mod config panels which implements a lot of
@@ -42,74 +48,361 @@ public abstract class AbstractConfigPanel implements ConfigPanel @@ -42,74 +48,361 @@ public abstract class AbstractConfigPanel implements ConfigPanel
42 } 48 }
43 49
44 /** 50 /**
45 - * Struct which keeps a control together with its callback object 51 + * A handle to an option text field, used to get and retrieve text and set
  52 + * the max length. It is possible to obtain the native text field as well,
  53 + * however caution should be used when doing so to avoid breaking the
  54 + * contract of the text field wrapper used in the config panel itself.
  55 + */
  56 + public interface ConfigTextField
  57 + {
  58 + /**
  59 + * Get the inner text field
  60 + */
  61 + public abstract GuiTextField getNativeTextField();
  62 +
  63 + /**
  64 + * Get the text field's text
  65 + */
  66 + public abstract String getText();
  67 +
  68 + /**
  69 + * Set the text field's text
  70 + *
  71 + * @param text text to set
  72 + * @return fluent interface
  73 + */
  74 + public abstract ConfigTextField setText(String text);
  75 +
  76 + /**
  77 + * Set a validation regex for this text box.
  78 + *
  79 + * @param regex Validation regex to use for this text field
  80 + * @param force If set to <tt>false</tt>, invalid values will only cause
  81 + * the text field to display an error when invalid text is present.
  82 + * If set to <tt>true</tt>, invalid values will be forcibly
  83 + * prohibited from being entered.
  84 + * @return fluent interfaces
  85 + */
  86 + public abstract ConfigTextField setRegex(String regex, boolean force);
  87 +
  88 + /**
  89 + * If the validation regex is not set, always returns true. Otherwise
  90 + * returns true if the current text value matches the validation regex.
  91 + *
  92 + * @return validation state of the current text value
  93 + */
  94 + public abstract boolean isValid();
  95 +
  96 + /**
  97 + * Set the max allowed string length, defaults to 32
46 * 98 *
47 - * @param <T> control type 99 + * @param maxLength max string length to use
  100 + * @return fluent interface
  101 + */
  102 + public abstract ConfigTextField setMaxLength(int maxLength);
  103 + }
  104 +
  105 + /**
  106 + * Base for config option handle structs
48 */ 107 */
49 - class ConfigOption<T extends GuiButton> 108 + static abstract class ConfigOption
50 { 109 {
51 - final GuiLabel label;  
52 - final T control;  
53 - final ConfigOptionListener<T> listener; 110 + void onTick()
  111 + {
  112 + }
  113 +
  114 + abstract void draw(Minecraft minecraft, int mouseX, int mouseY, float partialTicks);
  115 +
  116 + void mouseReleased(Minecraft minecraft, int mouseX, int mouseY)
  117 + {
  118 + }
  119 +
  120 + boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY)
  121 + {
  122 + return false;
  123 + }
54 124
55 - ConfigOption(GuiLabel label) 125 + boolean keyPressed(Minecraft minecraft, char keyChar, int keyCode)
  126 + {
  127 + return false;
  128 + }
  129 + }
  130 +
  131 + /**
  132 + * Struct for labels
  133 + */
  134 + static class ConfigOptionLabel extends ConfigOption
  135 + {
  136 + private final GuiLabel label;
  137 +
  138 + ConfigOptionLabel(GuiLabel label)
56 { 139 {
57 this.label = label; 140 this.label = label;
58 - this.control = null;  
59 - this.listener = null;  
60 } 141 }
61 142
62 - ConfigOption(T control, ConfigOptionListener<T> listener) 143 + @Override
  144 + void draw(Minecraft minecraft, int mouseX, int mouseY, float partialTicks)
  145 + {
  146 + this.label.drawLabel(minecraft, mouseX, mouseY);
  147 + }
  148 + }
  149 +
  150 + /**
  151 + * Struct which keeps a control together with its callback object
  152 + *
  153 + * @param <T> control type
  154 + */
  155 + static class ConfigOptionButton<T extends GuiButton> extends ConfigOption
  156 + {
  157 + private final T control;
  158 + private final ConfigOptionListener<T> listener;
  159 +
  160 + ConfigOptionButton(T control, ConfigOptionListener<T> listener)
63 { 161 {
64 - this.label = null;  
65 this.control = control; 162 this.control = control;
66 this.listener = listener; 163 this.listener = listener;
67 } 164 }
68 165
  166 + @Override
69 void draw(Minecraft minecraft, int mouseX, int mouseY, float partialTicks) 167 void draw(Minecraft minecraft, int mouseX, int mouseY, float partialTicks)
70 { 168 {
71 - if (this.label != null) 169 + this.control.drawButton(minecraft, mouseX, mouseY);
  170 + }
  171 +
  172 + @Override
  173 + boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY)
  174 + {
  175 + if (this.control.mousePressed(minecraft, mouseX, mouseY))
  176 + {
  177 + this.control.playPressSound(minecraft.getSoundHandler());
  178 + if (this.listener != null)
  179 + {
  180 + this.listener.actionPerformed(this.control);
  181 + }
  182 + return true;
  183 + }
  184 +
  185 + return false;
  186 + }
  187 +
  188 + @Override
  189 + void mouseReleased(Minecraft minecraft, int mouseX, int mouseY)
  190 + {
  191 + this.control.mouseReleased(mouseX, mouseY);
  192 + }
  193 + }
  194 +
  195 + /**
  196 + * Struct for text fields
  197 + */
  198 + class ConfigOptionTextField extends ConfigOption implements ConfigTextField
  199 + {
  200 + /**
  201 + * List for accessing via tab order
  202 + */
  203 + private final List<GuiConfigTextField> tabOrder;
  204 +
  205 + /**
  206 + * Tab index
  207 + */
  208 + private final int tabIndex;
  209 +
  210 + /**
  211 + * Inner text field
  212 + */
  213 + private final GuiConfigTextField textField;
  214 +
  215 + ConfigOptionTextField(List<GuiConfigTextField> tabOrder, GuiConfigTextField textField)
  216 + {
  217 + this.tabOrder = tabOrder;
  218 + this.tabIndex = tabOrder.indexOf(textField);
  219 + this.textField = textField;
  220 +
  221 + if (this.tabIndex == 0)
72 { 222 {
73 - this.label.drawLabel(minecraft, mouseX, mouseY); 223 + textField.setFocused(true);
  224 + }
74 } 225 }
75 226
76 - if (this.control != null) 227 + @Override
  228 + void onTick()
77 { 229 {
78 - this.control.drawButton(minecraft, mouseX, mouseY); 230 + this.textField.updateCursorCounter();
79 } 231 }
  232 +
  233 + @Override
  234 + void draw(Minecraft minecraft, int mouseX, int mouseY, float partialTicks)
  235 + {
  236 + this.textField.drawTextBox(mouseX, mouseY);
80 } 237 }
81 238
  239 + @Override
82 boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY) 240 boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY)
83 { 241 {
84 - if (this.control != null && this.control.mousePressed(minecraft, mouseX, mouseY)) 242 + this.textField.mouseClicked(mouseX, mouseY, 0);
  243 + if (this.textField.isFocused())
85 { 244 {
86 - this.control.playPressSound(minecraft.getSoundHandler());  
87 - if (this.listener != null) 245 + // Unfocus all other text fields
  246 + for (GuiTextField textField : this.tabOrder)
88 { 247 {
89 - this.listener.actionPerformed(this.control); 248 + if (textField != this.textField)
  249 + {
  250 + textField.setFocused(false);
  251 + }
90 } 252 }
  253 + }
  254 +
  255 + return false;
  256 + }
  257 +
  258 + @Override
  259 + boolean keyPressed(Minecraft minecraft, char keyChar, int keyCode)
  260 + {
  261 + if (!this.textField.isFocused())
  262 + {
  263 + return false;
  264 + }
  265 +
  266 + if (keyCode == Keyboard.KEY_TAB)
  267 + {
  268 + this.textField.setFocused(false);
  269 + int tabOrderSize = this.tabOrder.size();
  270 + this.tabOrder.get((this.tabIndex + (GuiScreen.isShiftKeyDown() ? -1 : 1) + tabOrderSize) % tabOrderSize).setFocused(true);
91 return true; 271 return true;
92 } 272 }
93 273
  274 + return this.textField.textboxKeyTyped(keyChar, keyCode);
  275 + }
  276 +
  277 + @Override
  278 + public GuiTextField getNativeTextField()
  279 + {
  280 + return this.textField;
  281 + }
  282 +
  283 + @Override
  284 + public String getText()
  285 + {
  286 + return this.textField.getText();
  287 + }
  288 +
  289 + @Override
  290 + public ConfigTextField setText(String text)
  291 + {
  292 + this.textField.setText(text);
  293 + return this;
  294 + }
  295 +
  296 + @Override
  297 + public ConfigTextField setMaxLength(int maxLength)
  298 + {
  299 + this.textField.setMaxStringLength(maxLength);
  300 + return this;
  301 + }
  302 +
  303 + @Override
  304 + public ConfigTextField setRegex(String regex, boolean force)
  305 + {
  306 + this.textField.setRegex(Pattern.compile(regex), force);;
  307 + return this;
  308 + }
  309 +
  310 + @Override
  311 + public boolean isValid()
  312 + {
  313 + return this.textField.isValid();
  314 + }
  315 + }
  316 +
  317 + /**
  318 + * Custom text field which supports "soft" validation by regex (draws red
  319 + * border and error message when invalid)
  320 + */
  321 + class GuiConfigTextField extends GuiTextField
  322 + {
  323 + private final FontRenderer fontRenderer;
  324 + private final int width, height;
  325 + private Pattern regex;
  326 + private boolean valid, drawing;
  327 +
  328 + GuiConfigTextField(int id, FontRenderer fontRenderer, int x, int y, int width, int height)
  329 + {
  330 + super(id, fontRenderer, x, y, width, height);
  331 + this.fontRenderer = fontRenderer;
  332 + this.width = width;
  333 + this.height = height;
  334 + this.setRegex(null, false);
  335 + }
  336 +
  337 + void setRegex(Pattern regex, boolean restrict)
  338 + {
  339 + if (restrict && regex != null)
  340 + {
  341 + this.setValidator((text) -> regex.matcher(text).matches());
  342 + this.regex = null;
  343 + }
  344 + else
  345 + {
  346 + this.setValidator((text) -> {
  347 + this.validate(text);
  348 + return true;
  349 + });
  350 + this.regex = regex;
  351 + this.validate(this.getText());
  352 + }
  353 + }
  354 +
  355 + private boolean validate(String text)
  356 + {
  357 + this.valid = (this.regex == null || this.regex.matcher(text).matches());
  358 + return true;
  359 + }
  360 +
  361 + boolean isValid()
  362 + {
  363 + return this.valid;
  364 + }
  365 +
  366 + @Override
  367 + public boolean getEnableBackgroundDrawing()
  368 + {
  369 + boolean bg = super.getEnableBackgroundDrawing();
  370 + if (bg && this.drawing && !this.isValid())
  371 + {
  372 + drawRect(this.xPosition - 1, this.yPosition - 1, this.xPosition + this.width + 1, this.yPosition + this.height + 1, 0xFFFF5555);
  373 + drawRect(this.xPosition, this.yPosition, this.xPosition + this.width, this.yPosition + this.height, 0xFF000000);
94 return false; 374 return false;
95 } 375 }
96 376
97 - void mouseReleased(Minecraft mc, int mouseX, int mouseY) 377 + return bg;
  378 + }
  379 +
  380 + public void drawTextBox(int mouseX, int mouseY)
98 { 381 {
99 - if (this.control != null) 382 + this.drawing = true;
  383 + super.drawTextBox();
  384 + if (!this.isValid())
  385 + {
  386 + drawRect(this.xPosition + this.width - 10, this.yPosition, this.xPosition + this.width, this.yPosition + this.height, 0x66000000);
  387 + this.fontRenderer.drawString("\247l!", this.xPosition + this.width - 6, this.yPosition + (this.height / 2) - 4, 0xFFFF5555);
  388 + if (mouseX >= this.xPosition && mouseX < this.xPosition + this.width && mouseY >= this.yPosition && mouseY < this.yPosition + this.height)
100 { 389 {
101 - this.control.mouseReleased(mouseX, mouseY); 390 + AbstractConfigPanel.this.drawHoveringText(I18n.format("gui.invalidvalue"), mouseX, mouseY);
  391 + }
102 } 392 }
  393 + this.drawing = false;
103 } 394 }
104 } 395 }
105 396
106 protected final Minecraft mc; 397 protected final Minecraft mc;
107 398
108 - private final List<ConfigOption<?>> options = new ArrayList<ConfigOption<?>>(); 399 + private final List<ConfigOption> options = new ArrayList<ConfigOption>();
  400 +
  401 + private final List<GuiConfigTextField> textFields = new ArrayList<GuiConfigTextField>();
109 402
110 private int contentHeight = 0; 403 private int contentHeight = 0;
111 404
112 - private ConfigOption<?> selected; 405 + private ConfigOption selected;
113 406
114 public AbstractConfigPanel() 407 public AbstractConfigPanel()
115 { 408 {
@@ -170,7 +463,7 @@ public abstract class AbstractConfigPanel implements ConfigPanel @@ -170,7 +463,7 @@ public abstract class AbstractConfigPanel implements ConfigPanel
170 label.addLine(line); 463 label.addLine(line);
171 } 464 }
172 this.contentHeight = Math.max(y + height, this.contentHeight); 465 this.contentHeight = Math.max(y + height, this.contentHeight);
173 - this.options.add(new ConfigOption<GuiButton>(label)); 466 + this.options.add(new ConfigOptionLabel(label));
174 } 467 }
175 468
176 /** 469 /**
@@ -185,12 +478,33 @@ public abstract class AbstractConfigPanel implements ConfigPanel @@ -185,12 +478,33 @@ public abstract class AbstractConfigPanel implements ConfigPanel
185 if (control != null) 478 if (control != null)
186 { 479 {
187 this.contentHeight = Math.max(control.yPosition + ((IGuiButton)control).getButtonHeight(), this.contentHeight); 480 this.contentHeight = Math.max(control.yPosition + ((IGuiButton)control).getButtonHeight(), this.contentHeight);
188 - this.options.add(new ConfigOption<T>(control, listener)); 481 + this.options.add(new ConfigOptionButton<T>(control, listener));
189 } 482 }
190 483
191 return control; 484 return control;
192 } 485 }
193 486
  487 + /**
  488 + * Add a text field to the panel, returns a handle through which the created
  489 + * text field can be accessed
  490 + *
  491 + * @param id control id
  492 + * @param x text field x position
  493 + * @param y text field y position
  494 + * @param width text field width
  495 + * @param height text field height
  496 + * @return text field handle
  497 + */
  498 + protected ConfigTextField addTextField(int id, int x, int y, int width, int height)
  499 + {
  500 + GuiConfigTextField textField = new GuiConfigTextField(id, this.mc.fontRendererObj, x + 2, y, width, height);
  501 + this.textFields.add(textField);
  502 +
  503 + ConfigOptionTextField configOption = new ConfigOptionTextField(this.textFields, textField);
  504 + this.options.add(configOption);
  505 + return configOption;
  506 + }
  507 +
194 @Override 508 @Override
195 public void onPanelResize(ConfigPanelHost host) 509 public void onPanelResize(ConfigPanelHost host)
196 { 510 {
@@ -199,12 +513,16 @@ public abstract class AbstractConfigPanel implements ConfigPanel @@ -199,12 +513,16 @@ public abstract class AbstractConfigPanel implements ConfigPanel
199 @Override 513 @Override
200 public void onTick(ConfigPanelHost host) 514 public void onTick(ConfigPanelHost host)
201 { 515 {
  516 + for (ConfigOption configOption : this.options)
  517 + {
  518 + configOption.onTick();
  519 + }
202 } 520 }
203 521
204 @Override 522 @Override
205 public void drawPanel(ConfigPanelHost host, int mouseX, int mouseY, float partialTicks) 523 public void drawPanel(ConfigPanelHost host, int mouseX, int mouseY, float partialTicks)
206 { 524 {
207 - for (ConfigOption<?> configOption : this.options) 525 + for (ConfigOption configOption : this.options)
208 { 526 {
209 configOption.draw(this.mc, mouseX, mouseY, partialTicks); 527 configOption.draw(this.mc, mouseX, mouseY, partialTicks);
210 } 528 }
@@ -219,7 +537,7 @@ public abstract class AbstractConfigPanel implements ConfigPanel @@ -219,7 +537,7 @@ public abstract class AbstractConfigPanel implements ConfigPanel
219 return; 537 return;
220 } 538 }
221 539
222 - for (ConfigOption<?> configOption : this.options) 540 + for (ConfigOption configOption : this.options)
223 { 541 {
224 if (configOption.mousePressed(this.mc, mouseX, mouseY)) 542 if (configOption.mousePressed(this.mc, mouseX, mouseY))
225 { 543 {
@@ -251,5 +569,22 @@ public abstract class AbstractConfigPanel implements ConfigPanel @@ -251,5 +569,22 @@ public abstract class AbstractConfigPanel implements ConfigPanel
251 host.close(); 569 host.close();
252 return; 570 return;
253 } 571 }
  572 +
  573 + for (ConfigOption configOption : this.options)
  574 + {
  575 + if (configOption.keyPressed(this.mc, keyChar, keyCode))
  576 + {
  577 + break;
  578 + }
  579 + }
  580 + }
  581 +
  582 + protected final void drawHoveringText(String text, int x, int y)
  583 + {
  584 + if (this.mc.currentScreen != null)
  585 + {
  586 + GuiLiteLoaderPanel.drawTooltip(this.mc.fontRendererObj, text, x, y, Integer.MAX_VALUE, Integer.MAX_VALUE, 0xFFFF5555, 0xB0000000);
  587 + RenderHelper.disableStandardItemLighting();
  588 + }
254 } 589 }
255 } 590 }
src/client/resources/mixins.liteloader.client.json
1 { 1 {
2 "required": true, 2 "required": true,
3 - "minVersion": "0.6", 3 + "minVersion": "0.7",
  4 + "compatibilityLevel": "JAVA_8",
4 "target": "@env(DEFAULT)", 5 "target": "@env(DEFAULT)",
5 "package": "com.mumfrey.liteloader.client.mixin", 6 "package": "com.mumfrey.liteloader.client.mixin",
6 "refmap": "mixins.liteloader.client.refmap.json", 7 "refmap": "mixins.liteloader.client.refmap.json",
src/main/java/com/mumfrey/liteloader/core/EnabledModsList.java
@@ -24,6 +24,34 @@ import com.google.gson.GsonBuilder; @@ -24,6 +24,34 @@ import com.google.gson.GsonBuilder;
24 */ 24 */
25 public final class EnabledModsList 25 public final class EnabledModsList
26 { 26 {
  27 + /**
  28 + * Tristate for enablement which allows us to determine whether mod is
  29 + * forcibly disabled by user or passively disabled by mod name filter
  30 + */
  31 + public enum Enabled
  32 + {
  33 + ENABLED(true),
  34 + DISABLED(false),
  35 + FILTERED(false);
  36 +
  37 + private final boolean value;
  38 +
  39 + private Enabled(boolean value)
  40 + {
  41 + this.value = value;
  42 + }
  43 +
  44 + public boolean booleanValue()
  45 + {
  46 + return this.value;
  47 + }
  48 +
  49 + public static Enabled of(Boolean value)
  50 + {
  51 + return value == null ? Enabled.FILTERED : (value ? Enabled.ENABLED : Enabled.DISABLED);
  52 + }
  53 + }
  54 +
27 @SuppressWarnings("unused") 55 @SuppressWarnings("unused")
28 private static final transient long serialVersionUID = -6449451105617763769L; 56 private static final transient long serialVersionUID = -6449451105617763769L;
29 57
@@ -45,7 +73,7 @@ public final class EnabledModsList @@ -45,7 +73,7 @@ public final class EnabledModsList
45 * the mods list because the command line is supposed to be an override 73 * the mods list because the command line is supposed to be an override
46 * rather than a new mask. These two values provide this behaviour. 74 * rather than a new mask. These two values provide this behaviour.
47 */ 75 */
48 - private transient Boolean defaultEnabledValue = Boolean.TRUE; 76 + private transient Enabled defaultEnabledValue = Enabled.ENABLED;
49 private transient boolean allowSave = true; 77 private transient boolean allowSave = true;
50 78
51 /** 79 /**
@@ -59,22 +87,27 @@ public final class EnabledModsList @@ -59,22 +87,27 @@ public final class EnabledModsList
59 } 87 }
60 88
61 /** 89 /**
62 - * Check whether a particular mod is enabled 90 + * Check whether a particular container is enabled
63 * 91 *
64 * @param profileName 92 * @param profileName
65 * @param identifier 93 * @param identifier
66 */ 94 */
67 public boolean isEnabled(String profileName, String identifier) 95 public boolean isEnabled(String profileName, String identifier)
68 { 96 {
  97 + return this.getEnabled(profileName, identifier).booleanValue();
  98 + }
  99 +
  100 + public Enabled getEnabled(String profileName, String identifier)
  101 + {
69 Map<String, Boolean> profile = this.getProfile(profileName); 102 Map<String, Boolean> profile = this.getProfile(profileName);
70 identifier = identifier.toLowerCase().trim(); 103 identifier = identifier.toLowerCase().trim();
71 104
72 - if (!profile.containsKey(identifier)) 105 + if (!profile.containsKey(identifier) && this.defaultEnabledValue != Enabled.FILTERED)
73 { 106 {
74 - profile.put(identifier, this.defaultEnabledValue); 107 + profile.put(identifier, this.defaultEnabledValue.booleanValue());
75 } 108 }
76 109
77 - return profile.get(identifier); 110 + return Enabled.of(profile.get(identifier));
78 } 111 }
79 112
80 /** 113 /**
@@ -87,7 +120,7 @@ public final class EnabledModsList @@ -87,7 +120,7 @@ public final class EnabledModsList
87 public void setEnabled(String profileName, String identifier, boolean enabled) 120 public void setEnabled(String profileName, String identifier, boolean enabled)
88 { 121 {
89 Map<String, Boolean> profile = this.getProfile(profileName); 122 Map<String, Boolean> profile = this.getProfile(profileName);
90 - profile.put(identifier.toLowerCase().trim(), Boolean.valueOf(enabled)); 123 + profile.put(identifier.toLowerCase().trim(), enabled ? Boolean.TRUE : Boolean.FALSE);
91 124
92 this.allowSave = true; 125 this.allowSave = true;
93 } 126 }
@@ -106,12 +139,9 @@ public final class EnabledModsList @@ -106,12 +139,9 @@ public final class EnabledModsList
106 { 139 {
107 if (modNameFilter != null) 140 if (modNameFilter != null)
108 { 141 {
109 - for (String modName : profile.keySet())  
110 - {  
111 - profile.put(modName, Boolean.FALSE);  
112 - } 142 + profile.clear();
113 143
114 - this.defaultEnabledValue = Boolean.FALSE; 144 + this.defaultEnabledValue = Enabled.FILTERED;
115 this.allowSave = false; 145 this.allowSave = false;
116 146
117 for (String filterEntry : modNameFilter) 147 for (String filterEntry : modNameFilter)
@@ -122,8 +152,8 @@ public final class EnabledModsList @@ -122,8 +152,8 @@ public final class EnabledModsList
122 } 152 }
123 catch (Exception ex) 153 catch (Exception ex)
124 { 154 {
125 - this.defaultEnabledValue = Boolean.TRUE;  
126 - this.allowSave = true; 155 + this.defaultEnabledValue = Enabled.ENABLED;
  156 +// this.allowSave = true;
127 } 157 }
128 } 158 }
129 159
@@ -135,7 +165,7 @@ public final class EnabledModsList @@ -135,7 +165,7 @@ public final class EnabledModsList
135 private Map<String, Boolean> getProfile(String profileName) 165 private Map<String, Boolean> getProfile(String profileName)
136 { 166 {
137 if (profileName == null) profileName = "default"; 167 if (profileName == null) profileName = "default";
138 - if (this.mods == null) this.mods = new TreeMap<String, TreeMap<String,Boolean>>(); 168 + if (this.mods == null) this.mods = new TreeMap<String, TreeMap<String, Boolean>>();
139 169
140 if (!this.mods.containsKey(profileName)) 170 if (!this.mods.containsKey(profileName))
141 { 171 {
src/main/java/com/mumfrey/liteloader/core/LiteLoader.java
@@ -532,6 +532,15 @@ public final class LiteLoader @@ -532,6 +532,15 @@ public final class LiteLoader
532 { 532 {
533 return "true".equals(System.getProperty("mcpenv")); 533 return "true".equals(System.getProperty("mcpenv"));
534 } 534 }
  535 +
  536 + /**
  537 + * Get whether the current running version is a snapshot build
  538 + */
  539 + public static boolean isSnapshot()
  540 + {
  541 + String branding = LiteLoader.getBranding();
  542 + return branding != null && branding.contains("SNAPSHOT");
  543 + }
535 544
536 /** 545 /**
537 * Dump debugging information to the console 546 * Dump debugging information to the console
src/main/java/com/mumfrey/liteloader/core/LiteLoaderUpdateSite.java
@@ -8,6 +8,10 @@ package com.mumfrey.liteloader.core; @@ -8,6 +8,10 @@ package com.mumfrey.liteloader.core;
8 import java.io.File; 8 import java.io.File;
9 import java.io.IOException; 9 import java.io.IOException;
10 import java.io.InputStream; 10 import java.io.InputStream;
  11 +import java.util.Date;
  12 +import java.util.Map;
  13 +import java.util.regex.Matcher;
  14 +import java.util.regex.Pattern;
11 15
12 import com.google.common.io.ByteSink; 16 import com.google.common.io.ByteSink;
13 import com.google.common.io.Files; 17 import com.google.common.io.Files;
@@ -21,6 +25,8 @@ public class LiteLoaderUpdateSite extends UpdateSite @@ -21,6 +25,8 @@ public class LiteLoaderUpdateSite extends UpdateSite
21 private static final String UPDATE_SITE_URL = "http://dl.liteloader.com/versions/"; 25 private static final String UPDATE_SITE_URL = "http://dl.liteloader.com/versions/";
22 private static final String UPDATE_SITE_VERSIONS_JSON = "versions.json"; 26 private static final String UPDATE_SITE_VERSIONS_JSON = "versions.json";
23 private static final String UPDATE_SITE_ARTEFACT_NAME = "com.mumfrey:liteloader"; 27 private static final String UPDATE_SITE_ARTEFACT_NAME = "com.mumfrey:liteloader";
  28 +
  29 + private static final Pattern SNAPSHOT_REGEX = Pattern.compile("^([0-9\\._]+)-SNAPSHOT-(r[0-9a-z]+)-(b([0-9]+))-(.*)$", Pattern.CASE_INSENSITIVE);
24 30
25 private String mcVersion; 31 private String mcVersion;
26 32
@@ -28,6 +34,10 @@ public class LiteLoaderUpdateSite extends UpdateSite @@ -28,6 +34,10 @@ public class LiteLoaderUpdateSite extends UpdateSite
28 private File jarFile = null; 34 private File jarFile = null;
29 35
30 private boolean updateForced = false; 36 private boolean updateForced = false;
  37 + private boolean isSnapshot = false;
  38 +
  39 + private int currentBuild, availableBuild;
  40 + private String snapshotDate = null;
31 41
32 public LiteLoaderUpdateSite(String targetVersion, long currentTimeStamp) 42 public LiteLoaderUpdateSite(String targetVersion, long currentTimeStamp)
33 { 43 {
@@ -36,6 +46,64 @@ public class LiteLoaderUpdateSite extends UpdateSite @@ -36,6 +46,64 @@ public class LiteLoaderUpdateSite extends UpdateSite
36 46
37 this.mcVersion = targetVersion; 47 this.mcVersion = targetVersion;
38 } 48 }
  49 +
  50 + @Override
  51 + public void beginUpdateCheck()
  52 + {
  53 + this.isSnapshot = LiteLoader.isSnapshot();
  54 + super.beginUpdateCheck();
  55 + }
  56 +
  57 + @Override
  58 + public boolean isSnapshot()
  59 + {
  60 + return this.isSnapshot;
  61 + }
  62 +
  63 + @Override
  64 + public String getAvailableVersion()
  65 + {
  66 + if (this.isSnapshot() && this.availableBuild > 0)
  67 + {
  68 + return String.valueOf(this.availableBuild);
  69 + }
  70 +
  71 + return super.getAvailableVersion();
  72 + }
  73 +
  74 + @Override
  75 + public String getAvailableVersionDate()
  76 + {
  77 + if (this.snapshotDate != null)
  78 + {
  79 + return this.snapshotDate;
  80 + }
  81 +
  82 + return super.getAvailableVersionDate();
  83 + }
  84 +
  85 + @Override
  86 + protected boolean compareArtefact(Map<?, ?> artefact, long bestTimeStamp, Long remoteTimeStamp)
  87 + {
  88 + if (this.isSnapshot())
  89 + {
  90 + String remoteBuild = artefact.get("build").toString();
  91 + String myBuild = LiteLoader.getBranding();
  92 +
  93 + Matcher remoteMatcher = LiteLoaderUpdateSite.SNAPSHOT_REGEX.matcher(remoteBuild);
  94 + Matcher myMatcher = LiteLoaderUpdateSite.SNAPSHOT_REGEX.matcher(myBuild);
  95 +
  96 + if (remoteMatcher.matches() && myMatcher.matches())
  97 + {
  98 + this.currentBuild = Integer.parseInt(myMatcher.group(4));
  99 + this.availableBuild = Integer.parseInt(remoteMatcher.group(4));
  100 + this.snapshotDate = String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM", new Date(remoteTimeStamp * 1000L));
  101 + return this.availableBuild > this.currentBuild;
  102 + }
  103 + }
  104 +
  105 + return super.compareArtefact(artefact, bestTimeStamp, remoteTimeStamp);
  106 + }
39 107
40 public boolean canForceUpdate(LoaderProperties properties) 108 public boolean canForceUpdate(LoaderProperties properties)
41 { 109 {
src/main/java/com/mumfrey/liteloader/core/runtime/Obf.java
@@ -26,14 +26,11 @@ public class Obf @@ -26,14 +26,11 @@ public class Obf
26 public static final Obf BakedProfilingHandlerList = new Obf("com.mumfrey.liteloader.core.event.ProfilingHandlerList$BakedList" ); 26 public static final Obf BakedProfilingHandlerList = new Obf("com.mumfrey.liteloader.core.event.ProfilingHandlerList$BakedList" );
27 public static final Obf PacketEvents = new Obf("com.mumfrey.liteloader.core.PacketEvents" ); 27 public static final Obf PacketEvents = new Obf("com.mumfrey.liteloader.core.PacketEvents" );
28 public static final Obf PacketEventsClient = new Obf("com.mumfrey.liteloader.client.PacketEventsClient" ); 28 public static final Obf PacketEventsClient = new Obf("com.mumfrey.liteloader.client.PacketEventsClient" );
29 - public static final Obf LoadingBar = new Obf("com.mumfrey.liteloader.client.gui.startup.LoadingBar" );  
30 public static final Obf GameProfile = new Obf("com.mojang.authlib.GameProfile" ); 29 public static final Obf GameProfile = new Obf("com.mojang.authlib.GameProfile" );
31 public static final Obf MinecraftMain = new Obf("net.minecraft.client.main.Main" ); 30 public static final Obf MinecraftMain = new Obf("net.minecraft.client.main.Main" );
32 public static final Obf MinecraftServer = new Obf("net.minecraft.server.MinecraftServer" ); 31 public static final Obf MinecraftServer = new Obf("net.minecraft.server.MinecraftServer" );
33 public static final Obf GL11 = new Obf("org.lwjgl.opengl.GL11" ); 32 public static final Obf GL11 = new Obf("org.lwjgl.opengl.GL11" );
34 public static final Obf RealmsMainScreen = new Obf("com.mojang.realmsclient.RealmsMainScreen" ); 33 public static final Obf RealmsMainScreen = new Obf("com.mojang.realmsclient.RealmsMainScreen" );
35 - public static final Obf init = new Obf("init" );  
36 - public static final Obf postInit = new Obf("postInit" );  
37 public static final Obf constructor = new Obf("<init>" ); 34 public static final Obf constructor = new Obf("<init>" );
38 35
39 // CHECKSTYLE:OFF 36 // CHECKSTYLE:OFF
src/main/java/com/mumfrey/liteloader/core/runtime/Packets.java
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 */ 5 */
6 package com.mumfrey.liteloader.core.runtime; 6 package com.mumfrey.liteloader.core.runtime;
7 7
  8 +import java.lang.reflect.Field;
8 import java.util.HashMap; 9 import java.util.HashMap;
9 import java.util.Map; 10 import java.util.Map;
10 11
@@ -157,125 +158,7 @@ public final class Packets extends Obf @@ -157,125 +158,7 @@ public final class Packets extends Obf
157 158
158 // CHECKSTYLE:ON 159 // CHECKSTYLE:ON
159 160
160 - public static final Packets[] packets = new Packets[] {  
161 - CPacketEncryptionResponse,  
162 - CPacketLoginStart,  
163 - SPacketDisconnectLogin,  
164 - SPacketEnableCompression,  
165 - SPacketEncryptionRequest,  
166 - SPacketLoginSuccess,  
167 - CPacketAnimation,  
168 - CPacketChatMessage,  
169 - CPacketClickWindow,  
170 - CPacketClientSettings,  
171 - CPacketClientStatus,  
172 - CPacketCloseWindow,  
173 - CPacketConfirmTeleport,  
174 - CPacketConfirmTransaction,  
175 - CPacketCreativeInventoryAction,  
176 - CPacketCustomPayload,  
177 - CPacketEnchantItem,  
178 - CPacketEntityAction,  
179 - CPacketHeldItemChange,  
180 - CPacketInput,  
181 - C00Handshake,  
182 - CPacketKeepAlive,  
183 - CPacketPlayer,  
184 - CPacketPlayerPosition,  
185 - CPacketPlayerPositionRotation,  
186 - CPacketPlayerRotation,  
187 - CPacketPlayerAbilities,  
188 - CPacketPlayerDigging,  
189 - CPacketPlayerTryUseItem,  
190 - CPacketPlayerTryUseItemOnBlock,  
191 - CPacketResourcePackStatus,  
192 - CPacketSpectate,  
193 - CPacketSteerBoat,  
194 - CPacketTabComplete,  
195 - CPacketUpdateSign,  
196 - CPacketUseEntity,  
197 - CPacketVehicleMove,  
198 - SPacketAnimation,  
199 - SPacketBlockAction,  
200 - SPacketBlockBreakAnim,  
201 - SPacketBlockChange,  
202 - SPacketCamera,  
203 - SPacketChangeGameState,  
204 - SPacketChat,  
205 - SPacketChunkData,  
206 - SPacketCloseWindow,  
207 - SPacketCollectItem,  
208 - SPacketCombatEvent,  
209 - SPacketConfirmTransaction,  
210 - SPacketCooldown,  
211 - SPacketCustomPayload,  
212 - SPacketCustomSound,  
213 - SPacketDestroyEntities,  
214 - SPacketDisconnect,  
215 - SPacketDisplayObjective,  
216 - SPacketEffect,  
217 - SPacketEntity,  
218 - S15PacketEntityRelMove,  
219 - S16PacketEntityLook,  
220 - S17PacketEntityLookMove,  
221 - SPacketEntityAttach,  
222 - SPacketEntityEffect,  
223 - SPacketEntityEquipment,  
224 - SPacketEntityHeadLook,  
225 - SPacketEntityMetadata,  
226 - SPacketEntityProperties,  
227 - SPacketEntityStatus,  
228 - SPacketEntityTeleport,  
229 - SPacketEntityVelocity,  
230 - SPacketExplosion,  
231 - SPacketHeldItemChange,  
232 - SPacketJoinGame,  
233 - SPacketKeepAlive,  
234 - SPacketMaps,  
235 - SPacketMoveVehicle,  
236 - SPacketMultiBlockChange,  
237 - SPacketOpenWindow,  
238 - SPacketParticles,  
239 - SPacketPlayerAbilities,  
240 - SPacketPlayerListHeaderFooter,  
241 - SPacketPlayerListItem,  
242 - SPacketPlayerPosLook,  
243 - SPacketRemoveEntityEffect,  
244 - SPacketResourcePackSend,  
245 - SPacketRespawn,  
246 - SPacketScoreboardObjective,  
247 - SPacketServerDifficulty,  
248 - SPacketSetExperience,  
249 - SPacketSetPassengers,  
250 - SPacketSetSlot,  
251 - SPacketSignEditorOpen,  
252 - SPacketSoundEffect,  
253 - SPacketSpawnExperienceOrb,  
254 - SPacketSpawnGlobalEntity,  
255 - SPacketSpawnMob,  
256 - SPacketSpawnObject,  
257 - SPacketSpawnPainting,  
258 - SPacketSpawnPlayer,  
259 - SPacketSpawnPosition,  
260 - SPacketStatistics,  
261 - SPacketTabComplete,  
262 - SPacketTeams,  
263 - SPacketTimeUpdate,  
264 - SPacketTitle,  
265 - SPacketUnloadChunk,  
266 - SPacketUpdateBossInfo,  
267 - SPacketUpdateHealth,  
268 - SPacketUpdateScore,  
269 - SPacketUpdateTileEntity,  
270 - SPacketUseBed,  
271 - SPacketWindowItems,  
272 - SPacketWindowProperty,  
273 - SPacketWorldBorder,  
274 - CPacketPing,  
275 - CPacketServerQuery,  
276 - SPacketPong,  
277 - SPacketServerInfo  
278 - }; 161 + public static final Packets[] packets = Packets.toArray();
279 162
280 private static int nextPacketIndex; 163 private static int nextPacketIndex;
281 164
@@ -295,6 +178,24 @@ public final class Packets extends Obf @@ -295,6 +178,24 @@ public final class Packets extends Obf
295 this.context = context; 178 this.context = context;
296 } 179 }
297 180
  181 + private static Packets[] toArray()
  182 + {
  183 + Field[] fields = Packets.class.getFields();
  184 + Packets[] packets = new Packets[Packets.nextPacketIndex];
  185 + for (int index = 0; index < Packets.nextPacketIndex; index++)
  186 + {
  187 + try
  188 + {
  189 + packets[index] = (Packets)fields[index].get(null);
  190 + }
  191 + catch (Exception ex)
  192 + {
  193 + throw new RuntimeException(ex);
  194 + }
  195 + }
  196 + return packets;
  197 + }
  198 +
298 public int getIndex() 199 public int getIndex()
299 { 200 {
300 return this.index; 201 return this.index;
src/main/java/com/mumfrey/liteloader/interfaces/PanelManager.java
@@ -88,4 +88,14 @@ public interface PanelManager&lt;TParentScreen&gt; extends TickObserver, PostRenderObs @@ -88,4 +88,14 @@ public interface PanelManager&lt;TParentScreen&gt; extends TickObserver, PostRenderObs
88 * Get whether "force update" is enabled 88 * Get whether "force update" is enabled
89 */ 89 */
90 public abstract boolean isForceUpdateEnabled(); 90 public abstract boolean isForceUpdateEnabled();
  91 +
  92 + /**
  93 + * Set whether "check for new snapshots" is enabled
  94 + */
  95 + public abstract void setCheckForSnapshotsEnabled(boolean checkForSnapshots);
  96 +
  97 + /**
  98 + * Get whether "check for new snapshots" is enabled
  99 + */
  100 + public abstract boolean isCheckForSnapshotsEnabled();
91 } 101 }
src/main/java/com/mumfrey/liteloader/launch/LoaderProperties.java
@@ -99,6 +99,7 @@ public interface LoaderProperties @@ -99,6 +99,7 @@ public interface LoaderProperties
99 public static final String OPTION_FORCE_UPDATE = "allowForceUpdate"; 99 public static final String OPTION_FORCE_UPDATE = "allowForceUpdate";
100 public static final String OPTION_UPDATE_CHECK_INTR = "updateCheckInterval"; 100 public static final String OPTION_UPDATE_CHECK_INTR = "updateCheckInterval";
101 public static final String OPTION_JINPUT_DISABLE = "disableJInput"; 101 public static final String OPTION_JINPUT_DISABLE = "disableJInput";
  102 + public static final String OPTION_CHECK_SNAPSHOTS = "checkForNewSnapshots";
102 103
103 // Enumerator properties 104 // Enumerator properties
104 public static final String OPTION_SEARCH_MODS = "search.mods"; 105 public static final String OPTION_SEARCH_MODS = "search.mods";
src/main/java/com/mumfrey/liteloader/modconfig/ConfigManager.java
@@ -17,6 +17,7 @@ import com.google.common.collect.Maps; @@ -17,6 +17,7 @@ import com.google.common.collect.Maps;
17 import com.google.common.io.Files; 17 import com.google.common.io.Files;
18 import com.mumfrey.liteloader.Configurable; 18 import com.mumfrey.liteloader.Configurable;
19 import com.mumfrey.liteloader.LiteMod; 19 import com.mumfrey.liteloader.LiteMod;
  20 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
20 21
21 /** 22 /**
22 * Registry where we keep the mod config panel classes and config file writers 23 * Registry where we keep the mod config panel classes and config file writers
@@ -165,6 +166,10 @@ public class ConfigManager @@ -165,6 +166,10 @@ public class ConfigManager
165 } 166 }
166 catch (InstantiationException ex) {} 167 catch (InstantiationException ex) {}
167 catch (IllegalAccessException ex) {} 168 catch (IllegalAccessException ex) {}
  169 + catch (Exception ex)
  170 + {
  171 + LiteLoaderLogger.severe("Error creating mod configuration panel <%s> for mod %s", this.configPanels.get(modClass), modClass);
  172 + }
168 173
169 // If instantiation fails, remove the panel 174 // If instantiation fails, remove the panel
170 this.configPanels.remove(modClass); 175 this.configPanels.remove(modClass);
src/main/java/com/mumfrey/liteloader/permissions/PermissionsManagerClient.java
@@ -47,37 +47,37 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi @@ -47,37 +47,37 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
47 * Permissions permissible which is a proxy for permissions that are common 47 * Permissions permissible which is a proxy for permissions that are common
48 * to all mods. 48 * to all mods.
49 */ 49 */
50 - private static Permissible allMods = new PermissibleAllMods();  
51 -  
52 - /**  
53 - * Minecraft instance  
54 - */  
55 - private GameEngine<?, ?> engine; 50 + private final static Permissible allMods = new PermissibleAllMods();
56 51
57 /** 52 /**
58 * List of registered client mods supporting permissions 53 * List of registered client mods supporting permissions
59 */ 54 */
60 - private Map<String, Permissible> registeredClientMods = new HashMap<String, Permissible>(); 55 + private final Map<String, Permissible> registeredClientMods = new HashMap<String, Permissible>();
61 56
62 /** 57 /**
63 * List of registered client permissions, grouped by mod 58 * List of registered client permissions, grouped by mod
64 */ 59 */
65 - private Map<Permissible, TreeSet<String>> registeredClientPermissions = new HashMap<Permissible, TreeSet<String>>(); 60 + private final Map<Permissible, TreeSet<String>> registeredClientPermissions = new HashMap<Permissible, TreeSet<String>>();
66 61
67 /** 62 /**
68 * Objects which listen to events generated by this object 63 * Objects which listen to events generated by this object
69 */ 64 */
70 - private Set<Permissible> permissibles = new HashSet<Permissible>(); 65 + private final Set<Permissible> permissibles = new HashSet<Permissible>();
71 66
72 /** 67 /**
73 * Local permissions, used when server permissions are not available 68 * Local permissions, used when server permissions are not available
74 */ 69 */
75 - private LocalPermissions localPermissions = new LocalPermissions(); 70 + private final LocalPermissions localPermissions = new LocalPermissions();
76 71
77 /** 72 /**
78 * Server permissions, indexed by mod 73 * Server permissions, indexed by mod
79 */ 74 */
80 - private Map<String, ServerPermissions> serverPermissions = new HashMap<String, ServerPermissions>(); 75 + private final Map<String, ServerPermissions> serverPermissions = new HashMap<String, ServerPermissions>();
  76 +
  77 + /**
  78 + * Minecraft instance
  79 + */
  80 + private GameEngine<?, ?> engine;
81 81
82 /** 82 /**
83 * Last time onTick was called, used to detect tamper condition if no ticks 83 * Last time onTick was called, used to detect tamper condition if no ticks
@@ -124,8 +124,11 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi @@ -124,8 +124,11 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
124 if (mod == null) mod = allMods; 124 if (mod == null) mod = allMods;
125 String modName = mod.getPermissibleModName(); 125 String modName = mod.getPermissibleModName();
126 126
127 - ServerPermissions modPermissions = this.serverPermissions.get(modName);  
128 - return modPermissions != null ? modPermissions : this.localPermissions; 127 + synchronized (this.serverPermissions)
  128 + {
  129 + ServerPermissions modPermissions = this.serverPermissions.get(modName);
  130 + return modPermissions != null ? modPermissions : this.localPermissions;
  131 + }
129 } 132 }
130 133
131 /* (non-Javadoc) 134 /* (non-Javadoc)
@@ -138,8 +141,11 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi @@ -138,8 +141,11 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
138 if (mod == null) mod = allMods; 141 if (mod == null) mod = allMods;
139 String modName = mod.getPermissibleModName(); 142 String modName = mod.getPermissibleModName();
140 143
141 - ServerPermissions modPermissions = this.serverPermissions.get(modName);  
142 - return modPermissions != null ? modPermissions.getReplicationTime() : 0; 144 + synchronized (this.serverPermissions)
  145 + {
  146 + ServerPermissions modPermissions = this.serverPermissions.get(modName);
  147 + return modPermissions != null ? modPermissions.getReplicationTime() : 0;
  148 + }
143 } 149 }
144 150
145 /** 151 /**
@@ -208,7 +214,10 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi @@ -208,7 +214,10 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
208 */ 214 */
209 protected void clearServerPermissions() 215 protected void clearServerPermissions()
210 { 216 {
211 - this.serverPermissions.clear(); 217 + synchronized (this.serverPermissions)
  218 + {
  219 + this.serverPermissions.clear();
  220 + }
212 221
213 for (Permissible permissible : this.permissibles) 222 for (Permissible permissible : this.permissibles)
214 { 223 {
@@ -264,7 +273,10 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi @@ -264,7 +273,10 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
264 } 273 }
265 else 274 else
266 { 275 {
267 - this.serverPermissions.remove(modName); 276 + synchronized (this.serverPermissions)
  277 + {
  278 + this.serverPermissions.remove(modName);
  279 + }
268 } 280 }
269 } 281 }
270 282
@@ -289,12 +301,15 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi @@ -289,12 +301,15 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
289 } 301 }
290 } 302 }
291 303
292 - for (Map.Entry<String, ServerPermissions> modPermissions : this.serverPermissions.entrySet()) 304 + synchronized (this.serverPermissions)
293 { 305 {
294 - if (!modPermissions.getValue().isValid()) 306 + for (Map.Entry<String, ServerPermissions> modPermissions : this.serverPermissions.entrySet())
295 { 307 {
296 - modPermissions.getValue().notifyRefreshPending();  
297 - this.sendPermissionQuery(this.registeredClientMods.get(modPermissions.getKey())); 308 + if (!modPermissions.getValue().isValid())
  309 + {
  310 + modPermissions.getValue().notifyRefreshPending();
  311 + this.sendPermissionQuery(this.registeredClientMods.get(modPermissions.getKey()));
  312 + }
298 } 313 }
299 } 314 }
300 315
@@ -336,7 +351,10 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi @@ -336,7 +351,10 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
336 351
337 if (modPermissions != null && modPermissions.getModName() != null) 352 if (modPermissions != null && modPermissions.getModName() != null)
338 { 353 {
339 - this.serverPermissions.put(modPermissions.getModName(), modPermissions); 354 + synchronized (this.serverPermissions)
  355 + {
  356 + this.serverPermissions.put(modPermissions.getModName(), modPermissions);
  357 + }
340 358
341 Permissible permissible = this.registeredClientMods.get(modPermissions.getModName()); 359 Permissible permissible = this.registeredClientMods.get(modPermissions.getModName());
342 if (permissible != null) permissible.onPermissionsChanged(this); 360 if (permissible != null) permissible.onPermissionsChanged(this);
src/main/java/com/mumfrey/liteloader/update/UpdateSite.java
@@ -160,6 +160,11 @@ public class UpdateSite implements Comparator&lt;Long&gt; @@ -160,6 +160,11 @@ public class UpdateSite implements Comparator&lt;Long&gt;
160 } 160 }
161 } 161 }
162 } 162 }
  163 +
  164 + public boolean isSnapshot()
  165 + {
  166 + return false;
  167 + }
163 168
164 /** 169 /**
165 * Gets whether a check is in progress 170 * Gets whether a check is in progress
@@ -319,7 +324,7 @@ public class UpdateSite implements Comparator&lt;Long&gt; @@ -319,7 +324,7 @@ public class UpdateSite implements Comparator&lt;Long&gt;
319 */ 324 */
320 private void handleVersionData(Object key, Map<?, ?> value) 325 private void handleVersionData(Object key, Map<?, ?> value)
321 { 326 {
322 - if ("artefacts".equals(key)) 327 + if ((this.isSnapshot() && "snapshots".equals(key)) || (!this.isSnapshot() && "artefacts".equals(key)))
323 { 328 {
324 if (value.containsKey(this.artefact)) 329 if (value.containsKey(this.artefact))
325 { 330 {
@@ -387,7 +392,7 @@ public class UpdateSite implements Comparator&lt;Long&gt; @@ -387,7 +392,7 @@ public class UpdateSite implements Comparator&lt;Long&gt;
387 this.availableVersion = artefact.get("version").toString(); 392 this.availableVersion = artefact.get("version").toString();
388 this.availableVersionDate = DateFormat.getDateTimeInstance().format(new Date(remoteTimeStamp * 1000L)); 393 this.availableVersionDate = DateFormat.getDateTimeInstance().format(new Date(remoteTimeStamp * 1000L));
389 this.availableVersionURL = this.createArtefactURL(artefact.get("file").toString()); 394 this.availableVersionURL = this.createArtefactURL(artefact.get("file").toString());
390 - this.updateAvailable = this.compareTimeStamps(bestTimeStamp, remoteTimeStamp); 395 + this.updateAvailable = this.compareArtefact(artefact, bestTimeStamp, remoteTimeStamp);
391 396
392 return true; 397 return true;
393 } 398 }
@@ -395,6 +400,11 @@ public class UpdateSite implements Comparator&lt;Long&gt; @@ -395,6 +400,11 @@ public class UpdateSite implements Comparator&lt;Long&gt;
395 return false; 400 return false;
396 } 401 }
397 402
  403 + protected boolean compareArtefact(Map<?, ?> artefact, long bestTimeStamp, Long remoteTimeStamp)
  404 + {
  405 + return this.compareTimeStamps(bestTimeStamp, remoteTimeStamp);
  406 + }
  407 +
398 /** 408 /**
399 * @param bestTimeStamp 409 * @param bestTimeStamp
400 * @param remoteTimeStamp 410 * @param remoteTimeStamp
src/main/resources/assets/liteloader/lang/en_us.lang
@@ -16,6 +16,9 @@ gui.settings.notabhide.help1=Only applies if the above option is also checked @@ -16,6 +16,9 @@ gui.settings.notabhide.help1=Only applies if the above option is also checked
16 gui.settings.forceupdate.label=Periodically Check For Updates 16 gui.settings.forceupdate.label=Periodically Check For Updates
17 gui.settings.forceupdate.help1=This option is §cexperimental§r and may not work properly 17 gui.settings.forceupdate.help1=This option is §cexperimental§r and may not work properly
18 gui.settings.forceupdate.help2=yet, it also enables the "force update" capability. 18 gui.settings.forceupdate.help2=yet, it also enables the "force update" capability.
  19 +gui.settings.checkforsnapshots.label=Check For New Snapshots
  20 +gui.settings.checkforsnapshots.help1=If this option is enabled, LiteLoader will check
  21 +gui.settings.checkforsnapshots.help2=for new SNAPSHOT releases each time it it launched
19 22
20 gui.about.taboptions=§nLiteLoader Panel Options 23 gui.about.taboptions=§nLiteLoader Panel Options
21 24
@@ -83,7 +86,11 @@ gui.log.uploadsuccess=Upload succeeded, log available at @@ -83,7 +86,11 @@ gui.log.uploadsuccess=Upload succeeded, log available at
83 gui.log.closedialog=Close 86 gui.log.closedialog=Close
84 87
85 gui.error.title=Startup errors for %s 88 gui.error.title=Startup errors for %s
  89 +gui.error.copytoclipboard=Copy error to clipboard
86 90
87 gui.error.tooltip=%d mod startup error(s) detected (%d critical) 91 gui.error.tooltip=%d mod startup error(s) detected (%d critical)
88 92
89 -gui.notifications.updateavailable=LiteLoader Update Available!  
90 \ No newline at end of file 93 \ No newline at end of file
  94 +gui.notifications.updateavailable=LiteLoader Update Available!
  95 +gui.notifications.newsnapshotavailable=!!New Snapshot Available:%nBuild #%s (%s)
  96 +
  97 +gui.invalidvalue=§cInvalid value!
91 \ No newline at end of file 98 \ No newline at end of file
src/main/resources/mixins.liteloader.core.json
1 { 1 {
2 "required": true, 2 "required": true,
3 - "minVersion": "0.5.3", 3 + "minVersion": "0.7",
  4 + "compatibilityLevel": "JAVA_8",
4 "target": "@env(DEFAULT)", 5 "target": "@env(DEFAULT)",
5 "package": "com.mumfrey.liteloader.common.mixin", 6 "package": "com.mumfrey.liteloader.common.mixin",
6 "refmap": "mixins.liteloader.core.refmap.json", 7 "refmap": "mixins.liteloader.core.refmap.json",