Merge '1.8' into 1.8.9

64898ce Fix NPE when saving recorded resource pack
70e3e54 Fix AbstractMethodError caused by FG2.0 not supporting lambdas (fixes #58)
3f2b3d6 Fix replacing source file in Replay Editor (fixes #57)
d2def94 Fix duration of replays corrupted because of JVM/OS crash
4deb374 Enable validation on focus change for all number input fields
5302bca Fix id and text of Replay Editor button in docs
057edcc Manually inject root cert for cross-signed LetsEncrypt cert in requests
This commit is contained in:
johni0702
2017-01-24 13:25:53 +01:00
14 changed files with 187 additions and 51 deletions

View File

@@ -56,7 +56,7 @@ Please use [Optifine](https://optifine.net/) instead.
### Custom Main Menu [custom-main-menu] ### Custom Main Menu [custom-main-menu]
The [Custom Main Menu](https://mods.curse.com/mc-mods/minecraft/226406-custom-main-menu) mod is often used in mod packs to customize their Main Menu with a button layout fitting the background image, links to their website / bug tracker and similar. The [Custom Main Menu](https://mods.curse.com/mc-mods/minecraft/226406-custom-main-menu) mod is often used in mod packs to customize their Main Menu with a button layout fitting the background image, links to their website / bug tracker and similar.
If you are familiar with it, the button ids for the Replay Mod are: **17890234** (text: `replaymod.gui.replayviewer`), **17890235** (text: `replaymod.gui.replayviewer`) and **17890236** (text: `replaymod.gui.replaycenter`) If you are familiar with it, the button ids for the Replay Mod are: **17890234** (text: `replaymod.gui.replayviewer`), **17890237** (text: `replaymod.gui.replayeditor`) and **17890236** (text: `replaymod.gui.replaycenter`)
Due to the nature of this Custom Main Menu mod, buttons added to the Main Menu by 3rd party mods like the **Replay Mod** will not show up by default. Due to the nature of this Custom Main Menu mod, buttons added to the Main Menu by 3rd party mods like the **Replay Mod** will not show up by default.
Thus, to access the Replay Viewer/Editor/Center, you need to manually configure the position for those buttons. Thus, to access the Replay Viewer/Editor/Center, you need to manually configure the position for those buttons.

View File

@@ -3,6 +3,7 @@ package com.replaymod.core.gui;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.replaymod.replaystudio.PacketData; import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.io.ReplayInputStream; import com.replaymod.replaystudio.io.ReplayInputStream;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData; import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.replay.ZipReplayFile;
@@ -45,15 +46,19 @@ public class RestoreReplayGui extends AbstractGuiScreen<RestoreReplayGui> {
ReplayMetaData metaData = replayFile.getMetaData(); ReplayMetaData metaData = replayFile.getMetaData();
if (metaData != null && metaData.getDuration() == 0) { if (metaData != null && metaData.getDuration() == 0) {
// Try to restore replay duration // Try to restore replay duration
try (ReplayInputStream in = replayFile.getPacketData()) { // We need to re-write the packet data in case there are any incomplete packets dangling at the end
try (ReplayInputStream in = replayFile.getPacketData();
ReplayOutputStream out = replayFile.writePacketData()) {
PacketData last = null; PacketData last = null;
while ((last = in.readPacket()) != null) { while ((last = in.readPacket()) != null) {
metaData.setDuration((int) last.getTime()); metaData.setDuration((int) last.getTime());
out.write(last);
} }
replayFile.writeMetaData(metaData);
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
} }
// Write back the actual duration
replayFile.writeMetaData(metaData);
} }
replayFile.save(); replayFile.save();
replayFile.close(); replayFile.close();

View File

@@ -27,8 +27,20 @@ import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@@ -51,6 +63,42 @@ public class Utils {
DEFAULT_THUMBNAIL = thumbnail; DEFAULT_THUMBNAIL = thumbnail;
} }
/**
* Neither the root certificate of LetsEncrypt nor the root that cross-signed it is included in the default
* Java keystore prior to 8u101.
* Therefore whenever a connection to the replaymod.com site is made, this SSLContext has to be used instead.
* It has been constructed to include the necessary root certificates.
* @see #SSL_SOCKET_FACTORY
*/
public static final SSLContext SSL_CONTEXT;
/**
* @see #SSL_CONTEXT
*/
public static final SSLSocketFactory SSL_SOCKET_FACTORY;
static {
// Largely from https://community.letsencrypt.org/t/134/37
try (InputStream in = Utils.class.getResourceAsStream("/dst_root_ca_x3.pem")){
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(in);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("1", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustManagerFactory.getTrustManagers(), null);
SSL_CONTEXT = ctx;
SSL_SOCKET_FACTORY = ctx.getSocketFactory();
} catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
throw new RuntimeException(e);
}
}
public static String convertSecondsToShortString(int seconds) { public static String convertSecondsToShortString(int seconds) {
int hours = seconds/(60*60); int hours = seconds/(60*60);
int min = seconds/60 - hours*60; int min = seconds/60 - hours*60;

View File

@@ -1,5 +1,6 @@
package com.replaymod.editor.gui; package com.replaymod.editor.gui;
import com.google.common.io.Files;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -34,6 +35,7 @@ import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -119,10 +121,21 @@ public class GuiReplayEditor extends GuiScreen {
public void save(File inputFile, PacketStream.FilterInfo...filters) { public void save(File inputFile, PacketStream.FilterInfo...filters) {
save(FilenameUtils.getBaseName(inputFile.getName()), (outputFile) -> { save(FilenameUtils.getBaseName(inputFile.getName()), (outputFile) -> {
Studio studio = new ReplayStudio(); Studio studio = new ReplayStudio();
File tmpDir = null;
try {
File actualOutputFile = outputFile;
if (outputFile.getCanonicalPath().equals(inputFile.getCanonicalPath())) {
// Input and output files are identical. Due to the way the ZipReplayFile stores its temporary
// data, the same replay file must not be opened twice for writing (tmp files will be deleted once
// either one is closed or, on Windows, will throw an exception when deleted).
tmpDir = Files.createTempDir();
outputFile = new File(tmpDir, "replay.mcpr");
LOGGER.debug("Output file is identical to input file, using temporary output file {} instead",
outputFile);
}
try (ReplayFile outputReplay = new ZipReplayFile(studio, inputFile, outputFile); try (ReplayFile outputReplay = new ZipReplayFile(studio, inputFile, outputFile);
ReplayOutputStream out = outputReplay.writePacketData()) { ReplayOutputStream out = outputReplay.writePacketData();
// The input replay file MUST be closed before saving the output file ReplayFile inputReplay = new ZipReplayFile(studio, inputFile);
try (ReplayFile inputReplay = new ZipReplayFile(studio, inputFile);
ReplayInputStream in = inputReplay.getPacketData()) { ReplayInputStream in = inputReplay.getPacketData()) {
ReplayMetaData metaData = inputReplay.getMetaData(); ReplayMetaData metaData = inputReplay.getMetaData();
PacketStream stream = studio.createReplayStream(in, true); PacketStream stream = studio.createReplayStream(in, true);
@@ -154,11 +167,21 @@ public class GuiReplayEditor extends GuiScreen {
// Update duration of new replay // Update duration of new replay
metaData.setDuration((int) lastTimestamp); metaData.setDuration((int) lastTimestamp);
outputReplay.writeMetaData(metaData); outputReplay.writeMetaData(metaData);
}
out.close(); out.close();
outputReplay.save(); outputReplay.save();
}
if (outputFile != actualOutputFile) {
LOGGER.debug("Moving temporary output file {} to {}");
FileUtils.forceDelete(actualOutputFile);
FileUtils.moveFile(outputFile, actualOutputFile);
}
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally {
if (tmpDir != null && !FileUtils.deleteQuietly(tmpDir)) {
LOGGER.warn("Failed to delete temporary directory {}", tmpDir);
}
} }
}); });
} }

View File

@@ -39,6 +39,10 @@ import static com.replaymod.editor.ReplayModEditor.LOGGER;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
public class GuiTrimPanel extends GuiPanel { public class GuiTrimPanel extends GuiPanel {
private static GuiNumberField newGuiNumberField() {
return new GuiNumberField().setMaxLength(2).setSize(20, 20).setValidateOnFocusChange(true);
}
// Special value indicating no replay files were found // Special value indicating no replay files were found
private static final File NO_REPLAY = new File("."); private static final File NO_REPLAY = new File(".");
// Special value for the initial "Select Marker Keyframe" entry // Special value for the initial "Select Marker Keyframe" entry
@@ -50,15 +54,15 @@ public class GuiTrimPanel extends GuiPanel {
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay()) .setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay())
.setToString(f -> f == NO_REPLAY ? "" : FilenameUtils.getBaseName(f.getName())); .setToString(f -> f == NO_REPLAY ? "" : FilenameUtils.getBaseName(f.getName()));
public final GuiNumberField startHour = new GuiNumberField().setSize(20, 20).setMaxLength(2); public final GuiNumberField startHour = newGuiNumberField();
public final GuiNumberField startMin = new GuiNumberField().setSize(20, 20).setMaxLength(2); public final GuiNumberField startMin = newGuiNumberField();
public final GuiNumberField startSec = new GuiNumberField().setSize(20, 20).setMaxLength(2); public final GuiNumberField startSec = newGuiNumberField();
public final GuiNumberField startMilli = newGuiNumberField().setSize(40, 20).setMaxLength(4); public final GuiNumberField startMilli = newGuiNumberField().setSize(40, 20).setMaxLength(4);
public final GuiDropdownMenu<Marker> startMarker = new GuiDropdownMenu<>(); public final GuiDropdownMenu<Marker> startMarker = new GuiDropdownMenu<>();
public final GuiNumberField endHour = new GuiNumberField().setSize(20, 20).setMaxLength(2); public final GuiNumberField endHour = newGuiNumberField();
public final GuiNumberField endMin = new GuiNumberField().setSize(20, 20).setMaxLength(2); public final GuiNumberField endMin = newGuiNumberField();
public final GuiNumberField endSec = new GuiNumberField().setSize(20, 20).setMaxLength(2); public final GuiNumberField endSec = newGuiNumberField();
public final GuiNumberField endMilli = newGuiNumberField().setSize(40, 20).setMaxLength(4); public final GuiNumberField endMilli = newGuiNumberField().setSize(40, 20).setMaxLength(4);
public final GuiDropdownMenu<Marker> endMarker = new GuiDropdownMenu<>(); public final GuiDropdownMenu<Marker> endMarker = new GuiDropdownMenu<>();

View File

@@ -17,6 +17,7 @@ import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.Loader;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import javax.net.ssl.HttpsURLConnection;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.net.URL; import java.net.URL;
@@ -24,8 +25,10 @@ import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
public class OpenEyeExtra implements Extra { public class OpenEyeExtra implements Extra {
private static final String DOWNLOAD_URL = "http://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION; private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION;
private static final Setting<Boolean> ASK_FOR_OPEN_EYE = new Setting<>("advanced", "askForOpenEye", null, true); private static final Setting<Boolean> ASK_FOR_OPEN_EYE = new Setting<>("advanced", "askForOpenEye", null, true);
private ReplayMod mod; private ReplayMod mod;
@@ -69,7 +72,9 @@ public class OpenEyeExtra implements Extra {
File targetFile = new File("mods/" + Loader.MC_VERSION, "OpenEye.jar"); File targetFile = new File("mods/" + Loader.MC_VERSION, "OpenEye.jar");
FileUtils.forceMkdir(targetFile.getParentFile()); FileUtils.forceMkdir(targetFile.getParentFile());
ReadableByteChannel in = Channels.newChannel(new URL(DOWNLOAD_URL).openStream()); HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();
connection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
ReadableByteChannel in = Channels.newChannel(connection.getInputStream());
FileChannel out = new FileOutputStream(targetFile).getChannel(); FileChannel out = new FileOutputStream(targetFile).getChannel();
out.transferFrom(in, 0, Long.MAX_VALUE); out.transferFrom(in, 0, Long.MAX_VALUE);
} catch (Throwable e) { } catch (Throwable e) {

View File

@@ -15,13 +15,19 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.net.ssl.HttpsURLConnection;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.BufferedInputStream;
import java.net.HttpURLConnection; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
public class ApiClient { public class ApiClient {
private static final Minecraft mc = Minecraft.getMinecraft(); private static final Minecraft mc = Minecraft.getMinecraft();
@@ -130,7 +136,11 @@ public class ApiClient {
QueryBuilder builder = new QueryBuilder(ReplayModApiMethods.get_thumbnail); QueryBuilder builder = new QueryBuilder(ReplayModApiMethods.get_thumbnail);
builder.put("id", file); builder.put("id", file);
URL url = new URL(builder.toString()); URL url = new URL(builder.toString());
return ImageIO.read(url); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
try (InputStream in = connection.getInputStream()) {
return ImageIO.read(in);
}
} }
private boolean cancelDownload = false; private boolean cancelDownload = false;
@@ -143,7 +153,8 @@ public class ApiClient {
builder.put("id", file); builder.put("id", file);
String url = builder.toString(); String url = builder.toString();
URL website = new URL(url); URL website = new URL(url);
HttpURLConnection con = (HttpURLConnection) website.openConnection(); HttpsURLConnection con = (HttpsURLConnection) website.openConnection();
con.setSSLSocketFactory(SSL_SOCKET_FACTORY);
int fileSize = con.getContentLength(); int fileSize = con.getContentLength();

View File

@@ -7,12 +7,14 @@ import com.google.gson.JsonParser;
import com.replaymod.online.api.replay.holders.ApiError; import com.replaymod.online.api.replay.holders.ApiError;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import javax.net.ssl.HttpsURLConnection;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.Map; import java.util.Map;
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
public class SimpleApiClient { public class SimpleApiClient {
private static final JsonParser jsonParser = new JsonParser(); private static final JsonParser jsonParser = new JsonParser();
@@ -72,10 +74,11 @@ public class SimpleApiClient {
// read response // read response
String responseContent = null; String responseContent = null;
InputStream is = null; InputStream is = null;
HttpURLConnection httpUrlConnection = null; HttpsURLConnection httpUrlConnection = null;
try { try {
URL url = new URL(urlString); URL url = new URL(urlString);
httpUrlConnection = (HttpURLConnection) url.openConnection(); httpUrlConnection = (HttpsURLConnection) url.openConnection();
httpUrlConnection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
httpUrlConnection.setRequestMethod("GET"); httpUrlConnection.setRequestMethod("GET");

View File

@@ -9,16 +9,18 @@ import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import javax.net.ssl.HttpsURLConnection;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
@RequiredArgsConstructor @RequiredArgsConstructor
public class FileUploader { public class FileUploader {
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
@@ -48,7 +50,8 @@ public class FileUploader {
postData += "&name=" + URLEncoder.encode(filename, "UTF-8"); postData += "&name=" + URLEncoder.encode(filename, "UTF-8");
String url = ReplayModApiMethods.upload_file + postData; String url = ReplayModApiMethods.upload_file + postData;
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); HttpsURLConnection con = (HttpsURLConnection) new URL(url).openConnection();
con.setSSLSocketFactory(SSL_SOCKET_FACTORY);
con.setUseCaches(false); con.setUseCaches(false);
con.setDoOutput(true); con.setDoOutput(true);
con.setRequestMethod("POST"); con.setRequestMethod("POST");

View File

@@ -2,7 +2,7 @@ package com.replaymod.online.api.replay;
public class ReplayModApiMethods { public class ReplayModApiMethods {
public static final String REPLAYMOD_BASE_URL = "http://ReplayMod.com/api/"; public static final String REPLAYMOD_BASE_URL = "https://ReplayMod.com/api/";
public static final String register = REPLAYMOD_BASE_URL+"register"; public static final String register = REPLAYMOD_BASE_URL+"register";
public static final String check_authkey = REPLAYMOD_BASE_URL+"check_authkey"; public static final String check_authkey = REPLAYMOD_BASE_URL+"check_authkey";

View File

@@ -9,6 +9,7 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreenWorking; import net.minecraft.client.gui.GuiScreenWorking;
import net.minecraft.client.gui.GuiYesNo; import net.minecraft.client.gui.GuiYesNo;
import net.minecraft.client.gui.GuiYesNoCallback;
import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.ServerList; import net.minecraft.client.multiplayer.ServerList;
import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetHandlerPlayClient;
@@ -26,6 +27,7 @@ import javax.annotation.Nonnull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
@@ -52,6 +54,9 @@ public class ResourcePackRecorder {
boolean doWrite = false; // Whether we are the first and have to write it boolean doWrite = false; // Whether we are the first and have to write it
synchronized (replayFile) { // Need to read, modify and write the resource pack index atomically synchronized (replayFile) { // Need to read, modify and write the resource pack index atomically
Map<Integer, String> index = replayFile.getResourcePackIndex(); Map<Integer, String> index = replayFile.getResourcePackIndex();
if (index == null) {
index = new HashMap<>();
}
if (!index.containsValue(hash)) { if (!index.containsValue(hash)) {
// Hash is unknown, we have to write the resource pack ourselves // Hash is unknown, we have to write the resource pack ourselves
doWrite = true; doWrite = true;
@@ -107,19 +112,24 @@ public class ResourcePackRecorder {
} else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) { } else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
} else { } else {
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo((result, id) -> { // Lambdas MUST NOT be used with methods that need re-obfuscation in FG prior to 2.2 (will result in AbstractMethodError)
//noinspection Convert2Lambda
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() {
@Override
public void confirmClicked(boolean result, int id) {
if (serverData != null) { if (serverData != null) {
serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED); serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
} }
if (result) { if (result) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash); ResourcePackRecorder.this.downloadResourcePackFuture(requestId, url, hash);
} else { } else {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED)); netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
} }
ServerList.func_147414_b(serverData); ServerList.func_147414_b(serverData);
mc.displayGuiScreen(null); mc.displayGuiScreen(null);
}
}, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0))); }, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0)));
} }
} }

View File

@@ -80,8 +80,8 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
} }
}).setMinSize(new Dimension(0, 20)).setValues(RenderSettings.EncodingPreset.values()); }).setMinSize(new Dimension(0, 20)).setValues(RenderSettings.EncodingPreset.values());
public final GuiNumberField videoWidth = new GuiNumberField().setSize(50, 20).setMinValue(1); public final GuiNumberField videoWidth = new GuiNumberField().setSize(50, 20).setMinValue(1).setValidateOnFocusChange(true);
public final GuiNumberField videoHeight = new GuiNumberField().setSize(50, 20).setMinValue(1); public final GuiNumberField videoHeight = new GuiNumberField().setSize(50, 20).setMinValue(1).setValidateOnFocusChange(true);
public final GuiSlider frameRateSlider = new GuiSlider().onValueChanged(new Runnable() { public final GuiSlider frameRateSlider = new GuiSlider().onValueChanged(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -93,7 +93,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(2)) .setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(2))
.addElements(new HorizontalLayout.Data(0.5), videoWidth, new GuiLabel().setText("*"), videoHeight); .addElements(new HorizontalLayout.Data(0.5), videoWidth, new GuiLabel().setText("*"), videoHeight);
public final GuiNumberField bitRateField = new GuiNumberField().setValue(10).setSize(50, 20); public final GuiNumberField bitRateField = new GuiNumberField().setValue(10).setSize(50, 20).setValidateOnFocusChange(true);
public final GuiDropdownMenu<String> bitRateUnit = new GuiDropdownMenu<String>() public final GuiDropdownMenu<String> bitRateUnit = new GuiDropdownMenu<String>()
.setSize(50, 20).setValues("bps", "kbps", "mbps").setSelected("mbps"); .setSize(50, 20).setValues("bps", "kbps", "mbps").setSelected("mbps");

View File

@@ -16,6 +16,10 @@ import org.lwjgl.input.Keyboard;
import org.lwjgl.util.ReadablePoint; import org.lwjgl.util.ReadablePoint;
public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> implements Typeable { public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> implements Typeable {
private static GuiNumberField newGuiNumberField() {
return new GuiNumberField().setSize(150, 20).setValidateOnFocusChange(true);
}
private final ReplayHandler replayHandler; private final ReplayHandler replayHandler;
private final Marker marker; private final Marker marker;
@@ -23,15 +27,15 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> imp
public final GuiTextField nameField = new GuiTextField().setSize(150, 20); public final GuiTextField nameField = new GuiTextField().setSize(150, 20);
// TODO: Replace with a min/sec/msec field // TODO: Replace with a min/sec/msec field
public final GuiNumberField timeField = new GuiNumberField().setSize(150, 20).setPrecision(0); public final GuiNumberField timeField = newGuiNumberField().setPrecision(0);
public final GuiNumberField xField = new GuiNumberField().setSize(150, 20).setPrecision(10); public final GuiNumberField xField = newGuiNumberField().setPrecision(10);
public final GuiNumberField yField = new GuiNumberField().setSize(150, 20).setPrecision(10); public final GuiNumberField yField = newGuiNumberField().setPrecision(10);
public final GuiNumberField zField = new GuiNumberField().setSize(150, 20).setPrecision(10); public final GuiNumberField zField = newGuiNumberField().setPrecision(10);
public final GuiNumberField yawField = new GuiNumberField().setSize(150, 20).setPrecision(5); public final GuiNumberField yawField = newGuiNumberField().setPrecision(5);
public final GuiNumberField pitchField = new GuiNumberField().setSize(150, 20).setPrecision(5); public final GuiNumberField pitchField = newGuiNumberField().setPrecision(5);
public final GuiNumberField rollField = new GuiNumberField().setSize(150, 20).setPrecision(5); public final GuiNumberField rollField = newGuiNumberField().setPrecision(5);
public final GuiPanel inputs = GuiPanel.builder() public final GuiPanel inputs = GuiPanel.builder()
.layout(new GridLayout().setColumns(2).setSpacingX(7).setSpacingY(3)) .layout(new GridLayout().setColumns(2).setSpacingX(7).setSpacingY(3))

View File

@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----