Browse Source

Updated error handling to survive server outages

master
JellySquid 3 years ago
parent
commit
d54b7bcdeb
25 changed files with 362 additions and 125 deletions
  1. 0
    1
      launcher-aether/config.json
  2. 20
    6
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/components/FlatButton.java
  3. 56
    20
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/components/NewsElement.java
  4. 83
    0
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/panels/BannerPanel.java
  5. 8
    1
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/panels/ProgressIndicatorPanel.java
  6. 13
    3
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/resources/CacheManager.java
  7. 29
    15
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/resources/LauncherIcons.java
  8. 64
    37
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/resources/NewsFeedManager.java
  9. 2
    2
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/views/OptionsView.java
  10. 83
    32
      launcher-aether/src/main/java/com/gildedgames/launcher/ui/views/game/PlayView.java
  11. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/add.png
  12. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/bug.png
  13. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/close.png
  14. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/gear.png
  15. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/maximize.png
  16. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/minimize.png
  17. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/play.png
  18. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/refresh.png
  19. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/remove.png
  20. 0
    0
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/switch_user.png
  21. BIN
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/warn.png
  22. BIN
      launcher-aether/src/main/resources/com/gildedgames/assets/icons/64/warn.png
  23. 0
    2
      launcher-fancy/accounts.dat
  24. 0
    1
      launcher-fancy/config.json
  25. 4
    5
      launcher/src/main/java/com/skcraft/concurrency/ObservableFuture.java

+ 0
- 1
launcher-aether/config.json View File

@@ -1 +0,0 @@
1
-{"offlineEnabled":true,"jvmPath":null,"jvmArgs":null,"minMemory":1024,"maxMemory":4096,"permGen":256,"windowWidth":854,"widowHeight":480,"proxyEnabled":false,"proxyHost":"localhost","proxyPort":8080,"proxyUsername":null,"proxyPassword":null,"gameKey":null}

+ 20
- 6
launcher-aether/src/main/java/com/gildedgames/launcher/ui/components/FlatButton.java View File

@@ -17,7 +17,7 @@ public class FlatButton extends JButton {
17 17
 		DISABLED(new Color(0x777777), new Color(0x777777), new Color(0x777777), new Color(0x444444)),
18 18
 		LIGHT(new Color(0x333d47), new Color(0x4d5c6b), new Color(0x333d47), Color.WHITE),
19 19
 		HIGHLIGHTED(new Color(0x2f68a2), new Color(0x3d8ee0), new Color(0x2f68a2), Color.WHITE),
20
-		TRANSPARENT(null, null, new Color(0x333d47), Color.WHITE);
20
+		TRANSPARENT(null, null, new Color(0, 0, 0, 40), Color.WHITE);
21 21
 
22 22
 		private final Color bgNormal, bgHover, bgPressed;
23 23
 
@@ -44,6 +44,10 @@ public class FlatButton extends JButton {
44 44
 		}
45 45
 	}
46 46
 
47
+	public enum AlignState {
48
+		LEFT, CENTER, RIGHT
49
+	}
50
+
47 51
 	@Getter
48 52
 	@Setter
49 53
 	private ImageIcon buttonIcon;
@@ -54,7 +58,7 @@ public class FlatButton extends JButton {
54 58
 
55 59
 	@Getter
56 60
 	@Setter
57
-	private boolean isLeftAligned;
61
+	private AlignState align = AlignState.CENTER;
58 62
 
59 63
 	private boolean hovered;
60 64
 
@@ -138,18 +142,28 @@ public class FlatButton extends JButton {
138 142
 		}
139 143
 
140 144
 		if (this.getButtonIcon() != null) {
141
-			int x;
145
+			int x = 0;
142 146
 
143
-			if (this.isLeftAligned()) {
147
+			if (this.getAlign() == AlignState.LEFT) {
144 148
 				x = 12;
145
-			} else {
149
+			} else if (this.getAlign() == AlignState.RIGHT) {
150
+				x = this.getWidth() - textWidth - 36;
151
+			} else if (this.getAlign() == AlignState.CENTER) {
146 152
 				x = (this.getWidth() / 2) - ((this.getButtonIcon().getIconWidth() + textWidth + 24) / 2) + 6;
147 153
 			}
148 154
 
149 155
 			g2.drawImage(this.getButtonIcon().getImage(), x, (this.getHeight() / 2) - (this.getButtonIcon().getIconHeight() / 2), null);
150 156
 			g2.drawString(this.getText(), x + 24, (this.getHeight() / 2) - (textHeight / 2) + fontMetrics.getAscent());
151 157
 		} else {
152
-			int x = this.isLeftAligned() ? 12 : (this.getWidth() / 2) - (textWidth / 2);
158
+			int x = 0;
159
+
160
+			if (this.getAlign() == AlignState.LEFT) {
161
+				x = 12;
162
+			} else if (this.getAlign() == AlignState.CENTER) {
163
+				x = (this.getWidth() / 2) - (textWidth / 2);
164
+			} else if (this.getAlign() == AlignState.RIGHT) {
165
+				x = this.getWidth() - textWidth - 24;
166
+			}
153 167
 
154 168
 			g2.drawString(this.getText(), x, (this.getHeight() / 2) - (textHeight / 2) + fontMetrics.getAscent());
155 169
 		}

+ 56
- 20
launcher-aether/src/main/java/com/gildedgames/launcher/ui/components/NewsElement.java View File

@@ -1,10 +1,15 @@
1 1
 package com.gildedgames.launcher.ui.components;
2 2
 
3 3
 import com.gildedgames.launcher.ui.resources.LauncherFonts;
4
+import com.gildedgames.launcher.ui.resources.LauncherIcons;
4 5
 import com.gildedgames.launcher.ui.resources.NewsFeedManager;
6
+import com.google.common.util.concurrent.FutureCallback;
7
+import com.google.common.util.concurrent.Futures;
8
+import com.google.common.util.concurrent.ListenableFuture;
9
+import com.skcraft.launcher.util.SwingExecutor;
5 10
 
11
+import javax.annotation.Nullable;
6 12
 import javax.swing.JComponent;
7
-import javax.swing.SwingUtilities;
8 13
 import java.awt.*;
9 14
 import java.awt.event.MouseAdapter;
10 15
 import java.awt.event.MouseEvent;
@@ -15,10 +20,14 @@ import java.text.DateFormat;
15 20
 import java.text.SimpleDateFormat;
16 21
 
17 22
 public class NewsElement extends JComponent {
18
-	private static final Color TRANSPARENT = new Color(0, 0, 0, 1);
23
+	private static final Color TRANSPARENT = new Color(0, 0, 0, 1),
24
+			BACKGROUND = new Color(40, 40, 40);
19 25
 
20 26
 	private static final Font FONT_TITLE = LauncherFonts.OPEN_SANS_REGULAR.deriveFont(18.0f),
21
-			FONT_DATE = LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f);
27
+			FONT_DATE = LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f),
28
+			FONT_ERROR = LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f);
29
+
30
+	private static final Image WARNING_ICON = LauncherIcons.load("com/gildedgames/assets/icons/64/warn.png");
22 31
 
23 32
 	private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MMMM dd, yyyy");
24 33
 
@@ -28,15 +37,31 @@ public class NewsElement extends JComponent {
28 37
 
29 38
 	private BufferedImage image;
30 39
 
40
+	private boolean failed;
41
+
31 42
 	public NewsElement(NewsFeedManager resourceCacheManager, NewsFeedManager.NewsDataElement data, boolean isSmall) {
32 43
 		this.data = data;
33 44
 
34
-		resourceCacheManager.getImage(data.getImages().get("launcher_preview"), value -> {
35
-			this.image = value;
45
+		ListenableFuture<NewsFeedManager.ImageResult> future = resourceCacheManager.getImage(data.getImages().get("launcher_preview"));
46
+
47
+		Futures.addCallback(future, new FutureCallback<NewsFeedManager.ImageResult>() {
48
+			@Override
49
+			public void onSuccess(@Nullable NewsFeedManager.ImageResult result) {
50
+				if (result == null) {
51
+					return;
52
+				}
36 53
 
37
-			SwingUtilities.invokeLater(this::repaint);
54
+				NewsElement.this.image = result.getImage();
55
+			}
56
+
57
+			@Override
58
+			public void onFailure(Throwable t) {
59
+				NewsElement.this.failed = true;
60
+			}
38 61
 		});
39 62
 
63
+		future.addListener(this::repaint, SwingExecutor.INSTANCE);
64
+
40 65
 		this.setPreferredSize(isSmall ? new Dimension(291, 145) : new Dimension(440, 220));
41 66
 
42 67
 		this.addMouseListener(new MouseAdapter() {
@@ -84,6 +109,31 @@ public class NewsElement extends JComponent {
84 109
 
85 110
 		if (this.image != null) {
86 111
 			g2.drawImage(this.image, 0, 0, this.getWidth(), this.getHeight(), null);
112
+		} else {
113
+			g2.setColor(BACKGROUND);
114
+			g2.fillRect(0, 0, this.getWidth(), this.getHeight());
115
+
116
+			if (this.failed) {
117
+				Composite prevComposite = g2.getComposite();
118
+
119
+				AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
120
+				g2.setComposite(composite);
121
+
122
+				g2.setFont(FONT_ERROR);
123
+
124
+				FontMetrics f = g2.getFontMetrics();
125
+
126
+				String str = "Failed to load image";
127
+
128
+				int width = f.stringWidth(str);
129
+
130
+				g2.drawImage(WARNING_ICON, (this.getWidth() / 2) - 32, (this.getHeight() / 2) - 32, null);
131
+				g2.setColor(Color.WHITE);
132
+
133
+				g2.drawString(str, (this.getWidth() / 2) - (width / 2), (this.getHeight() / 2) + 40);
134
+
135
+				g2.setComposite(prevComposite);
136
+			}
87 137
 		}
88 138
 
89 139
 		if (this.hovered) {
@@ -102,18 +152,4 @@ public class NewsElement extends JComponent {
102 152
 		g2.setColor(Color.LIGHT_GRAY);
103 153
 		g2.drawString(DATE_FORMAT.format(this.data.getDate()), 8, this.getHeight() - 12);
104 154
 	}
105
-//
106
-//	private BufferedImage resizeImage(BufferedImage source, int targetWidth, int targetHeight) {
107
-//
108
-//		BufferedImage dest = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
109
-//
110
-//		Graphics2D g2 = (Graphics2D) dest.getGraphics();
111
-//		g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
112
-//		g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
113
-//		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
114
-//
115
-//		g2.drawImage(source, 0, 0, requiredWidth, requiredHeight, null);
116
-//
117
-//		return dest;
118
-//	}
119 155
 }

+ 83
- 0
launcher-aether/src/main/java/com/gildedgames/launcher/ui/panels/BannerPanel.java View File

@@ -0,0 +1,83 @@
1
+package com.gildedgames.launcher.ui.panels;
2
+
3
+import com.gildedgames.launcher.ui.components.FlatButton;
4
+import com.gildedgames.launcher.ui.resources.LauncherFonts;
5
+import com.gildedgames.launcher.ui.resources.LauncherIcons;
6
+import net.miginfocom.swing.MigLayout;
7
+
8
+import javax.swing.*;
9
+import java.awt.Color;
10
+import java.awt.Dimension;
11
+import java.awt.event.ActionEvent;
12
+import java.awt.event.ActionListener;
13
+
14
+public class BannerPanel extends JPanel {
15
+	public enum BannerType {
16
+		INFO(new Color(0x455A64)),
17
+		ERROR(new Color(0xc62828));
18
+
19
+		private final Color color;
20
+
21
+		BannerType(Color color) {
22
+			this.color = color;
23
+		}
24
+	}
25
+
26
+	private boolean closeable = false;
27
+
28
+	private JLabel label, icon;
29
+
30
+	private FlatButton buttonAction;
31
+
32
+	private Runnable onClickRunnable;
33
+
34
+	public BannerPanel() {
35
+		this.initComponents();
36
+	}
37
+
38
+	private void initComponents() {
39
+		this.setLayout(new MigLayout("fill, insets 10 14 14 10", "[]8[]push[]", "[grow]"));
40
+
41
+		this.label = new JLabel();
42
+		this.label.setFont(LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f));
43
+		this.label.setForeground(Color.WHITE);
44
+
45
+		this.icon = new JLabel();
46
+
47
+		this.buttonAction = new FlatButton("Close", LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f));
48
+		this.buttonAction.setStyle(FlatButton.ButtonStyle.TRANSPARENT);
49
+		this.buttonAction.setPreferredSize(new Dimension(120, 24));
50
+		this.buttonAction.setAlign(FlatButton.AlignState.RIGHT);
51
+		this.buttonAction.setBorder(BorderFactory.createEmptyBorder());
52
+		this.buttonAction.addActionListener(e -> {
53
+			if (this.onClickRunnable != null) {
54
+				this.onClickRunnable.run();
55
+			}
56
+		});
57
+
58
+		this.add(icon);
59
+		this.add(label);
60
+		this.add(buttonAction);
61
+	}
62
+
63
+	public void update(ImageIcon icon, String text, BannerType type) {
64
+		this.label.setText(text);
65
+		this.icon.setIcon(icon);
66
+
67
+		this.setBackground(type.color);
68
+
69
+		this.bindActionHandler(null, "Close", null);
70
+
71
+		this.setVisible(true);
72
+	}
73
+
74
+	public void bindActionHandler(ImageIcon icon, String label, Runnable clickHandler) {
75
+		this.onClickRunnable = clickHandler;
76
+		this.buttonAction.setText(label);
77
+		this.buttonAction.setButtonIcon(icon);
78
+	}
79
+
80
+	public void close() {
81
+		this.setVisible(false);
82
+	}
83
+}

+ 8
- 1
launcher-aether/src/main/java/com/gildedgames/launcher/ui/panels/ProgressIndicatorPanel.java View File

@@ -62,7 +62,10 @@ public class ProgressIndicatorPanel extends JPanel implements IProgressReporter
62 62
 
63 63
 			private void end() {
64 64
 				if (ProgressIndicatorPanel.this.future.isDone()) {
65
-					ProgressIndicatorPanel.this.updateTimer.cancel();
65
+					if (ProgressIndicatorPanel.this.updateTimer != null) {
66
+						ProgressIndicatorPanel.this.updateTimer.cancel();
67
+						ProgressIndicatorPanel.this.updateTimer = null;
68
+					}
66 69
 
67 70
 					ProgressIndicatorPanel.this.setVisible(false);
68 71
 				}
@@ -70,6 +73,10 @@ public class ProgressIndicatorPanel extends JPanel implements IProgressReporter
70 73
 		}, SwingExecutor.INSTANCE);
71 74
 
72 75
 		SwingUtilities.invokeLater(() -> {
76
+			if (ProgressIndicatorPanel.this.future.isDone()) {
77
+				return;
78
+			}
79
+
73 80
 			this.updateTimer = new Timer();
74 81
 			this.updateTimer.scheduleAtFixedRate(new ProgressIndicatorPanel.UpdateProgressTask(this), 10, 250);
75 82
 

+ 13
- 3
launcher-aether/src/main/java/com/gildedgames/launcher/ui/resources/CacheManager.java View File

@@ -1,12 +1,22 @@
1 1
 package com.gildedgames.launcher.ui.resources;
2 2
 
3
+import com.google.common.util.concurrent.ListenableFuture;
4
+import com.google.common.util.concurrent.ListeningExecutorService;
5
+import com.google.common.util.concurrent.MoreExecutors;
6
+
7
+import java.util.concurrent.Callable;
3 8
 import java.util.concurrent.ExecutorService;
4 9
 import java.util.concurrent.Executors;
10
+import java.util.concurrent.Future;
5 11
 
6 12
 public class CacheManager {
7
-	private static final ExecutorService service = Executors.newSingleThreadExecutor();
13
+	private static final ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
14
+
15
+	public static ListenableFuture<?> submitTask(Runnable runnable) {
16
+		return service.submit(runnable);
17
+	}
8 18
 
9
-	public static void submitTask(Runnable runnable) {
10
-		service.submit(runnable);
19
+	public static <T> ListenableFuture<T> submitTask(Callable<T> callable) {
20
+		return service.submit(callable);
11 21
 	}
12 22
 }

+ 29
- 15
launcher-aether/src/main/java/com/gildedgames/launcher/ui/resources/LauncherIcons.java View File

@@ -5,6 +5,7 @@ import lombok.extern.java.Log;
5 5
 
6 6
 import javax.imageio.ImageIO;
7 7
 import javax.swing.ImageIcon;
8
+import java.awt.Image;
8 9
 import java.io.IOException;
9 10
 import java.io.InputStream;
10 11
 
@@ -20,30 +21,42 @@ public class LauncherIcons {
20 21
 
21 22
 	public static final ImageIcon SWITCH_USER;
22 23
 
23
-	public static final ImageIcon BUG;
24
+	public static final ImageIcon BUG, WARN;
24 25
 
25 26
 	public static final ImageIcon WINDOW_MINIMIZE, WINDOW_CLOSE, WINDOW_MAXIMIZE;
26 27
 
27 28
 	public static final ImageIcon WINDOW_ICON;
28 29
 
29 30
 	static {
30
-		GEAR = load("com/gildedgames/assets/icons/gear.png");
31
-		ADD = load("com/gildedgames/assets/icons/add.png");
32
-		REMOVE = load("com/gildedgames/assets/icons/remove.png");
33
-		REFRESH = load("com/gildedgames/assets/icons/refresh.png");
34
-		PLAY = load("com/gildedgames/assets/icons/play.png");
31
+		GEAR = loadIcon("com/gildedgames/assets/icons/16/gear.png");
32
+		ADD = loadIcon("com/gildedgames/assets/icons/16/add.png");
33
+		REMOVE = loadIcon("com/gildedgames/assets/icons/16/remove.png");
34
+		REFRESH = loadIcon("com/gildedgames/assets/icons/16/refresh.png");
35
+		PLAY = loadIcon("com/gildedgames/assets/icons/16/play.png");
35 36
 
36
-		SWITCH_USER = load("com/gildedgames/assets/icons/switch_user.png");
37
-		BUG = load("com/gildedgames/assets/icons/bug.png");
37
+		SWITCH_USER = loadIcon("com/gildedgames/assets/icons/16/switch_user.png");
38
+		BUG = loadIcon("com/gildedgames/assets/icons/16/bug.png");
39
+		WARN = loadIcon("com/gildedgames/assets/icons/16/warn.png");
38 40
 
39
-		WINDOW_MINIMIZE = load("com/gildedgames/assets/icons/minimize.png");
40
-		WINDOW_CLOSE = load("com/gildedgames/assets/icons/close.png");
41
-		WINDOW_MAXIMIZE = load("com/gildedgames/assets/icons/maximize.png");
41
+		WINDOW_MINIMIZE = loadIcon("com/gildedgames/assets/icons/16/minimize.png");
42
+		WINDOW_CLOSE = loadIcon("com/gildedgames/assets/icons/16/close.png");
43
+		WINDOW_MAXIMIZE = loadIcon("com/gildedgames/assets/icons/16/maximize.png");
42 44
 
43
-		WINDOW_ICON = load("com/gildedgames/assets/titlebar/window-icon.png");
45
+		WINDOW_ICON = loadIcon("com/gildedgames/assets/titlebar/window-icon.png");
44 46
 	}
45 47
 
46
-	private static ImageIcon load(String path) {
48
+	public static ImageIcon loadIcon(String path) {
49
+		Image image = load(path);
50
+
51
+		if (image == null) {
52
+			return null;
53
+		}
54
+
55
+		return new ImageIcon(image);
56
+	}
57
+
58
+
59
+	public static Image load(String path) {
47 60
 		try {
48 61
 			InputStream stream = LauncherFrame.class.getClassLoader().getResourceAsStream(path);
49 62
 
@@ -51,10 +64,11 @@ public class LauncherIcons {
51 64
 				throw new IOException("Couldn't open stream");
52 65
 			}
53 66
 
54
-			return new ImageIcon(ImageIO.read(stream));
67
+			return ImageIO.read(stream);
55 68
 		} catch (IOException e) {
56 69
 			e.printStackTrace();
57
-			log.severe("Couldn't load icon " + path);
70
+
71
+			log.severe("Couldn't load image " + path);
58 72
 		}
59 73
 
60 74
 		return null;

+ 64
- 37
launcher-aether/src/main/java/com/gildedgames/launcher/ui/resources/NewsFeedManager.java View File

@@ -1,13 +1,19 @@
1 1
 package com.gildedgames.launcher.ui.resources;
2 2
 
3 3
 import com.fasterxml.jackson.annotation.JsonProperty;
4
+import com.google.common.util.concurrent.FutureCallback;
5
+import com.google.common.util.concurrent.Futures;
6
+import com.google.common.util.concurrent.ListenableFuture;
4 7
 import com.skcraft.concurrency.Callback;
8
+import com.skcraft.concurrency.ObservableFuture;
5 9
 import com.skcraft.launcher.Launcher;
6 10
 import com.skcraft.launcher.persistence.Persistence;
7 11
 import com.skcraft.launcher.util.HttpRequest;
12
+import lombok.AllArgsConstructor;
8 13
 import lombok.Getter;
9 14
 import org.apache.commons.io.IOUtils;
10 15
 
16
+import javax.annotation.Nullable;
11 17
 import javax.imageio.ImageIO;
12 18
 import java.awt.image.BufferedImage;
13 19
 import java.io.*;
@@ -17,6 +23,8 @@ import java.nio.file.Paths;
17 23
 import java.util.Date;
18 24
 import java.util.List;
19 25
 import java.util.Map;
26
+import java.util.concurrent.Callable;
27
+import java.util.concurrent.Future;
20 28
 
21 29
 public class NewsFeedManager {
22 30
 	private static final String METADATA_URL = "https://files.gildedgames.com/minecraft/launcher/news/latest.json",
@@ -41,65 +49,75 @@ public class NewsFeedManager {
41 49
 		return manager;
42 50
 	}
43 51
 
44
-	public void refresh(Callback<NewsFeed> callback, boolean force) {
45
-		if (!force && this.feed.expires > System.currentTimeMillis()) {
46
-			if (callback != null) {
47
-				callback.handle(this.feed);
52
+	public ListenableFuture<NewsFeed> refresh(boolean force) {
53
+		ListenableFuture<NewsFeed> future = CacheManager.submitTask(() -> {
54
+			if (!force && NewsFeedManager.this.feed.expires > System.currentTimeMillis()) {
55
+				return null;
48 56
 			}
49 57
 
50
-			return;
51
-		}
52
-
53
-		CacheManager.submitTask(() -> {
54 58
 			try {
55
-				NewsFeed feed = HttpRequest.get(HttpRequest.url(METADATA_URL))
59
+				NewsFeed feed1 = HttpRequest.get(HttpRequest.url(METADATA_URL))
56 60
 						.execute()
57 61
 						.expectResponseCode(200)
58 62
 						.returnContent()
59 63
 						.asJson(NewsFeed.class);
60 64
 
61
-				feed.expires = System.currentTimeMillis() + CACHE_TIME;
65
+				feed1.expires = System.currentTimeMillis() + CACHE_TIME;
66
+			} catch (Exception e) {
67
+				e.printStackTrace();
68
+			}
69
+
70
+			return feed;
71
+		});
62 72
 
63
-				this.feed = feed;
73
+		Futures.addCallback(future, new FutureCallback<NewsFeed>() {
74
+			@Override
75
+			public void onSuccess(@Nullable NewsFeed result) {
76
+				NewsFeedManager.this.feed = result;
77
+				NewsFeedManager.this.save();
78
+			}
64 79
 
65
-				if (callback != null) {
66
-					callback.handle(feed);
67
-				}
80
+			@Override
81
+			public void onFailure(Throwable t) {
68 82
 
69
-				this.save();
70
-			} catch (Exception e) {
71
-				e.printStackTrace();
72 83
 			}
73 84
 		});
74
-	}
75 85
 
86
+		return future;
87
+	}
76 88
 
77
-	public void getImage(String resource, Callback<BufferedImage> callback) {
78
-		CacheManager.submitTask(() -> {
79
-			try {
80
-				byte[] data = this.tryLoadImage(resource);
89
+	public ListenableFuture<ImageResult> getImage(String resource) {
90
+		ListenableFuture<ImageResult> future = CacheManager.submitTask(() -> {
91
+			byte[] data = this.tryLoadImage(resource);
81 92
 
82
-				if (data == null) {
83
-					data = HttpRequest.get(HttpRequest.url(IMAGE_PROVIDER_URL + resource))
84
-							.execute()
85
-							.expectResponseCode(200)
86
-							.returnContent()
87
-							.asBytes();
88
-				}
93
+			if (data == null) {
94
+				data = HttpRequest.get(HttpRequest.url(IMAGE_PROVIDER_URL + resource))
95
+						.execute()
96
+						.expectResponseCode(200)
97
+						.returnContent()
98
+						.asBytes();
99
+			}
89 100
 
90
-				BufferedImage image;
101
+			BufferedImage image;
91 102
 
92
-				try (ByteArrayInputStream input = new ByteArrayInputStream(data)) {
93
-					image = ImageIO.read(input);
94
-				}
103
+			try (ByteArrayInputStream input = new ByteArrayInputStream(data)) {
104
+				image = ImageIO.read(input);
105
+			}
95 106
 
96
-				callback.handle(image);
107
+			return new ImageResult(image, data);
108
+		});
97 109
 
98
-				this.trySaveImage(resource, data);
99
-			} catch (Exception e) {
100
-				e.printStackTrace();
110
+		Futures.addCallback(future, new FutureCallback<ImageResult>() {
111
+			@Override
112
+			public void onSuccess(@Nullable ImageResult result) {
113
+				NewsFeedManager.this.trySaveImage(resource, result.getData());
101 114
 			}
115
+
116
+			@Override
117
+			public void onFailure(Throwable t) { }
102 118
 		});
119
+
120
+		return future;
103 121
 	}
104 122
 
105 123
 	private byte[] tryLoadImage(String resource) {
@@ -143,6 +161,15 @@ public class NewsFeedManager {
143 161
 		Persistence.commitAndForget(this);
144 162
 	}
145 163
 
164
+	@AllArgsConstructor
165
+	public static class ImageResult {
166
+		@Getter
167
+		private final BufferedImage image;
168
+
169
+		@Getter
170
+		private final byte[] data;
171
+	}
172
+
146 173
 	public static class NewsFeed {
147 174
 		@JsonProperty
148 175
 		@Getter

+ 2
- 2
launcher-aether/src/main/java/com/gildedgames/launcher/ui/views/OptionsView.java View File

@@ -150,8 +150,8 @@ public class OptionsView extends JPanel {
150 150
 			this.add(dataDirectoryString, c);
151 151
 
152 152
 			c.ipady = 0;
153
-			c.gridx = 2;
154
-			c.gridy = 1;
153
+			c.gridx = 0;
154
+			c.gridy = 2;
155 155
 			c.weightx = 0.0D;
156 156
 			c.weighty = 1.0D;
157 157
 			c.anchor = GridBagConstraints.WEST;

+ 83
- 32
launcher-aether/src/main/java/com/gildedgames/launcher/ui/views/game/PlayView.java View File

@@ -6,15 +6,16 @@ import com.gildedgames.launcher.ui.LauncherFrame;
6 6
 import com.gildedgames.launcher.ui.components.FlatButton;
7 7
 import com.gildedgames.launcher.ui.components.NewsElement;
8 8
 import com.gildedgames.launcher.ui.components.UserIndicator;
9
+import com.gildedgames.launcher.ui.panels.BannerPanel;
9 10
 import com.gildedgames.launcher.ui.panels.ImagePanel;
10 11
 import com.gildedgames.launcher.ui.panels.ProgressIndicatorPanel;
11 12
 import com.gildedgames.launcher.ui.resources.LauncherFonts;
12 13
 import com.gildedgames.launcher.ui.resources.LauncherIcons;
13 14
 import com.gildedgames.launcher.ui.resources.NewsFeedManager;
14
-import com.gildedgames.launcher.ui.views.ProgressView;
15 15
 import com.gildedgames.launcher.ui.views.account.AccountListView;
16 16
 import com.google.common.util.concurrent.FutureCallback;
17 17
 import com.google.common.util.concurrent.Futures;
18
+import com.google.common.util.concurrent.ListenableFuture;
18 19
 import com.skcraft.concurrency.ObservableFuture;
19 20
 import com.skcraft.concurrency.ProgressObservable;
20 21
 import com.skcraft.launcher.Instance;
@@ -31,6 +32,7 @@ import com.skcraft.launcher.util.SwingExecutor;
31 32
 import lombok.Getter;
32 33
 import net.miginfocom.swing.MigLayout;
33 34
 
35
+import javax.annotation.Nullable;
34 36
 import javax.swing.*;
35 37
 import java.awt.BorderLayout;
36 38
 import java.awt.Color;
@@ -64,6 +66,10 @@ public class PlayView extends JPanel {
64 66
 
65 67
 	private ProgressIndicatorPanel progressIndicatorPanel;
66 68
 
69
+	private BannerPanel bannerPanel;
70
+
71
+	private JPanel news;
72
+
67 73
 	@Getter
68 74
 	private ConsolePanel consolePanel;
69 75
 
@@ -83,7 +89,7 @@ public class PlayView extends JPanel {
83 89
 
84 90
 		this.init();
85 91
 
86
-		SwingUtilities.invokeLater(this::loadInstances);
92
+		SwingUtilities.invokeLater(this::refresh);
87 93
 	}
88 94
 
89 95
 	private void init() {
@@ -97,7 +103,7 @@ public class PlayView extends JPanel {
97 103
 		this.switchUserButton = new FlatButton("Switch user", LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f));
98 104
 		this.switchUserButton.setStyle(FlatButton.ButtonStyle.TRANSPARENT);
99 105
 		this.switchUserButton.setButtonIcon(LauncherIcons.SWITCH_USER);
100
-		this.switchUserButton.setLeftAligned(true);
106
+		this.switchUserButton.setAlign(FlatButton.AlignState.LEFT);
101 107
 		this.switchUserButton.setBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9));
102 108
 
103 109
 		this.selfUpdateButton = new FlatButton(SharedLocale.tr("launcher.updateLauncher"), LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f));
@@ -108,7 +114,7 @@ public class PlayView extends JPanel {
108 114
 		FlatButton optionsButton = new FlatButton("Options", LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f));
109 115
 		optionsButton.setBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9));
110 116
 		optionsButton.setStyle(FlatButton.ButtonStyle.TRANSPARENT);
111
-		optionsButton.setLeftAligned(true);
117
+		optionsButton.setAlign(FlatButton.AlignState.LEFT);
112 118
 		optionsButton.setButtonIcon(LauncherIcons.GEAR);
113 119
 		optionsButton.addActionListener(e -> this.frame.showOptions());
114 120
 
@@ -117,10 +123,17 @@ public class PlayView extends JPanel {
117 123
 		profilesLabel.setForeground(new Color(160, 160, 160));
118 124
 		profilesLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
119 125
 
120
-		JPanel left = new JPanel(new MigLayout("fill, insets 0", "[fill]", "[]4[]0[]12[]12[]0[]"));
126
+		FlatButton refreshButton = new FlatButton("Refresh", LauncherFonts.OPEN_SANS_REGULAR.deriveFont(12.0f));
127
+		refreshButton.setStyle(FlatButton.ButtonStyle.TRANSPARENT);
128
+		refreshButton.setAlign(FlatButton.AlignState.LEFT);
129
+		refreshButton.setButtonIcon(LauncherIcons.REFRESH);
130
+		refreshButton.addActionListener(e -> this.refresh());
131
+
132
+		JPanel left = new JPanel(new MigLayout("fill, insets 0", "[fill]", "[]4[]0[]12[]8[]12[]0[]0"));
121 133
 		left.add(this.userIndicator, "wrap");
122 134
 		left.add(this.switchUserButton, "wrap");
123 135
 		left.add(optionsButton, "wrap");
136
+		left.add(refreshButton, "wrap");
124 137
 		left.add(profilesLabel, "wrap");
125 138
 		left.add(this.instancesTable, "grow, push, wrap");
126 139
 		left.add(this.launchButton, "wrap");
@@ -164,25 +177,8 @@ public class PlayView extends JPanel {
164 177
 		newsLayout.setHgap(7);
165 178
 		newsLayout.setVgap(7);
166 179
 
167
-		JPanel news = new JPanel(newsLayout);
168
-		news.setOpaque(false);
169
-
170
-		this.frame.getNewsFeedManager().refresh(value -> SwingUtilities.invokeLater(() -> {
171
-			news.removeAll();
172
-
173
-			int i= 0;
174
-
175
-			for (NewsFeedManager.NewsDataElement e : value.getNews()) {
176
-				NewsElement element = new NewsElement(this.frame.getNewsFeedManager(), e, i > 1);
177
-
178
-				news.add(element);
179
-
180
-				i++;
181
-			}
182
-
183
-			news.revalidate();
184
-			news.repaint();
185
-		}), true);
180
+		this.news = new JPanel(newsLayout);
181
+		this.news.setOpaque(false);
186 182
 
187 183
 		right.add(news, BorderLayout.CENTER);
188 184
 
@@ -190,6 +186,10 @@ public class PlayView extends JPanel {
190 186
 
191 187
 		right.add(this.progressIndicatorPanel, BorderLayout.SOUTH);
192 188
 
189
+		this.bannerPanel = new BannerPanel();
190
+
191
+		right.add(this.bannerPanel, BorderLayout.NORTH);
192
+
193 193
 		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
194 194
 		splitPane.setResizeWeight(0.0D);
195 195
 		splitPane.setDividerLocation(250);
@@ -239,6 +239,25 @@ public class PlayView extends JPanel {
239 239
 		});
240 240
 	}
241 241
 
242
+	private void layoutNewsTiles(NewsFeedManager.NewsFeed feed) {
243
+		SwingUtilities.invokeLater(() -> {
244
+			this.news.removeAll();
245
+
246
+			int i= 0;
247
+
248
+			for (NewsFeedManager.NewsDataElement e : feed.getNews()) {
249
+				NewsElement element = new NewsElement(this.frame.getNewsFeedManager(), e, i > 1);
250
+
251
+				this.news.add(element);
252
+
253
+				i++;
254
+			}
255
+
256
+			this.news.revalidate();
257
+			this.news.repaint();
258
+		});
259
+	}
260
+
242 261
 	public void updateUserIndicator(Account account) {
243 262
 		this.userIndicator.setAccount(account);
244 263
 	}
@@ -304,7 +323,7 @@ public class PlayView extends JPanel {
304 323
 		}
305 324
 
306 325
 		menuItem = new JMenuItem(SharedLocale.tr("launcher.refreshList"));
307
-		menuItem.addActionListener(e -> this.loadInstances());
326
+		menuItem.addActionListener(e -> this.refresh());
308 327
 		popup.add(menuItem);
309 328
 
310 329
 		popup.show(component, x, y);
@@ -318,7 +337,7 @@ public class PlayView extends JPanel {
318 337
 		ObservableFuture<Instance> future = this.launcher.getInstanceTasks().delete(this.frame, instance);
319 338
 
320 339
 		// Update the list of instances after updating
321
-		future.addListener(this::loadInstances, SwingExecutor.INSTANCE);
340
+		future.addListener(this::refresh, SwingExecutor.INSTANCE);
322 341
 	}
323 342
 
324 343
 	private void confirmHardUpdate(Instance instance) {
@@ -335,21 +354,53 @@ public class PlayView extends JPanel {
335 354
 		}, SwingExecutor.INSTANCE);
336 355
 	}
337 356
 
338
-	private void loadInstances() {
339
-		ObservableFuture<InstanceList> future = this.launcher.getInstanceTasks().reloadInstances();
357
+	private void refresh() {
358
+		ObservableFuture<InstanceList> refreshInstancesFuture = this.launcher.getInstanceTasks().reloadInstances();
340 359
 
341
-		ProgressView progressView = new ProgressView(this.frame, future, future, "Checking for updates");
342
-		this.frame.getLauncherLayout().show(progressView);
360
+		this.bannerPanel.update(LauncherIcons.REFRESH, "Checking for the latest updates", BannerPanel.BannerType.INFO);
343 361
 
344
-		future.addListener(() -> {
362
+		Futures.addCallback(refreshInstancesFuture, new FutureCallback<InstanceList>() {
363
+			@Override
364
+			public void onSuccess(@Nullable InstanceList result) {
365
+				PlayView.this.bannerPanel.close();
366
+			}
367
+
368
+			@Override
369
+			public void onFailure(Throwable t) {
370
+				PlayView.this.bannerPanel.update(LauncherIcons.WARN, "There was a problem checking for updates.", BannerPanel.BannerType.ERROR);
371
+				PlayView.this.bannerPanel.bindActionHandler(LauncherIcons.REFRESH, "Refresh", PlayView.this::refresh);
372
+			}
373
+		});
374
+
375
+		refreshInstancesFuture.addListener(() -> {
345 376
 			this.instancesModel.update();
377
+
346 378
 			if (this.instancesTable.getRowCount() > 0) {
347 379
 				this.instancesTable.setRowSelectionInterval(0, 0);
348 380
 			}
381
+
349 382
 			this.requestFocus();
383
+
384
+			this.refreshNews();
350 385
 		}, SwingExecutor.INSTANCE);
351 386
 
352
-		SwingHelper.addErrorDialogCallback(this.frame, future);
387
+//		SwingHelper.addErrorDialogCallback(this.frame, refreshInstancesFuture);
388
+	}
389
+
390
+	private void refreshNews() {
391
+		ListenableFuture<NewsFeedManager.NewsFeed> refreshNewsFuture = this.frame.getNewsFeedManager().refresh(false);
392
+
393
+		Futures.addCallback(refreshNewsFuture, new FutureCallback<NewsFeedManager.NewsFeed>() {
394
+			@Override
395
+			public void onSuccess(@Nullable NewsFeedManager.NewsFeed result) {
396
+				PlayView.this.layoutNewsTiles(result);
397
+			}
398
+
399
+			@Override
400
+			public void onFailure(Throwable t) {
401
+
402
+			}
403
+		});
353 404
 	}
354 405
 
355 406
 	private void launch() {

launcher-aether/src/main/resources/com/gildedgames/assets/icons/add.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/add.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/bug.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/bug.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/close.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/close.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/gear.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/gear.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/maximize.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/maximize.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/minimize.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/minimize.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/play.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/play.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/refresh.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/refresh.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/remove.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/remove.png View File


launcher-aether/src/main/resources/com/gildedgames/assets/icons/switch_user.png → launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/switch_user.png View File


BIN
launcher-aether/src/main/resources/com/gildedgames/assets/icons/16/warn.png View File


BIN
launcher-aether/src/main/resources/com/gildedgames/assets/icons/64/warn.png View File


+ 0
- 2
launcher-fancy/accounts.dat View File

@@ -1,2 +0,0 @@
1
-{Ê^ÌÐà÷ô~E‚g3U�ßùO}Òj®Éç�»ýPG—^2�ÈOÑ“`XóÚÑÏ1Mç]³wï©QeåE£ Ü“TAÓ÷Bщ°ÂÝc™38tn9E±Væò½X^æb
2
-èm—¼ÉˆEù›[

+ 0
- 1
launcher-fancy/config.json View File

@@ -1 +0,0 @@
1
-{"offlineEnabled":true,"jvmPath":null,"jvmArgs":null,"minMemory":1024,"maxMemory":4096,"permGen":256,"windowWidth":854,"widowHeight":480,"proxyEnabled":false,"proxyHost":"localhost","proxyPort":8080,"proxyUsername":null,"proxyPassword":null,"gameKey":null}

+ 4
- 5
launcher/src/main/java/com/skcraft/concurrency/ObservableFuture.java View File

@@ -9,10 +9,7 @@ package com.skcraft.concurrency;
9 9
 import com.google.common.util.concurrent.ListenableFuture;
10 10
 import lombok.NonNull;
11 11
 
12
-import java.util.concurrent.ExecutionException;
13
-import java.util.concurrent.Executor;
14
-import java.util.concurrent.TimeUnit;
15
-import java.util.concurrent.TimeoutException;
12
+import java.util.concurrent.*;
16 13
 
17 14
 /**
18 15
  * A pair of ProgressObservable and ListenableFuture.
@@ -35,7 +32,9 @@ public class ObservableFuture<V> implements ListenableFuture<V>, ProgressObserva
35 32
         this.observable = observable;
36 33
     }
37 34
 
38
-    @Override
35
+
36
+
37
+	@Override
39 38
     public boolean cancel(boolean mayInterruptIfRunning) {
40 39
         return future.cancel(mayInterruptIfRunning);
41 40
     }

Loading…
Cancel
Save