diff --git a/core/src/bms/player/beatoraja/Config.java b/core/src/bms/player/beatoraja/Config.java
index 9353927a..f0548c06 100644
--- a/core/src/bms/player/beatoraja/Config.java
+++ b/core/src/bms/player/beatoraja/Config.java
@@ -188,6 +188,7 @@ public class Config implements Validatable {
private String overrideDownloadURL = "";
private String downloadDirectory = DEFAULT_DOWNLOAD_DIRECTORY;
public static final String DEFAULT_DOWNLOAD_DIRECTORY = "http_download";
+ private boolean autoSwitchHttpDownloadSource = true;
private int irSendCount = 5;
@@ -826,7 +827,15 @@ public void setObsAction(String stateName, String actionName) {
}
}
- public boolean validate() {
+ public boolean isAutoSwitchHttpDownloadSource() {
+ return autoSwitchHttpDownloadSource;
+ }
+
+ public void setAutoSwitchHttpDownloadSource(boolean autoSwitchHttpDownloadSource) {
+ this.autoSwitchHttpDownloadSource = autoSwitchHttpDownloadSource;
+ }
+
+ public boolean validate() {
displaymode = (displaymode != null) ? displaymode : DisplayMode.WINDOW;
resolution = (resolution != null) ? resolution : Resolution.HD;
diff --git a/core/src/bms/player/beatoraja/MainController.java b/core/src/bms/player/beatoraja/MainController.java
index 461db229..8a33c86a 100644
--- a/core/src/bms/player/beatoraja/MainController.java
+++ b/core/src/bms/player/beatoraja/MainController.java
@@ -508,8 +508,7 @@ public void create() {
}
if (config.isEnableHttp()) {
- HttpDownloadSource httpDownloadSource = HttpDownloadProcessor.DOWNLOAD_SOURCES.get(config.getDownloadSource()).build(config);
- httpDownloadProcessor = new HttpDownloadProcessor(this, httpDownloadSource, config.getDownloadDirectory());
+ httpDownloadProcessor = new HttpDownloadProcessor(this, config);
DownloadTaskState.initialize(httpDownloadProcessor);
DownloadTaskMenu.setProcessor(httpDownloadProcessor);
}
diff --git a/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.fxml b/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.fxml
index d9b6b93e..8e75ba5b 100644
--- a/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.fxml
+++ b/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.fxml
@@ -419,6 +419,7 @@
+
diff --git a/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.java b/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.java
index c76f8855..a488dd68 100644
--- a/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.java
+++ b/core/src/bms/player/beatoraja/launcher/PlayConfigurationView.java
@@ -2,7 +2,6 @@
import java.awt.Desktop;
import java.io.File;
-import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -16,29 +15,20 @@
import bms.player.beatoraja.external.ScoreDataImporter;
import bms.tool.mdprocessor.HttpDownloadProcessor;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.databind.ObjectMapper;
import javafx.animation.AnimationTimer;
-import javafx.beans.binding.Bindings;
-import org.apache.commons.lang3.compare.ComparableUtils;
import bms.model.Mode;
import bms.player.beatoraja.*;
import bms.player.beatoraja.play.JudgeAlgorithm;
-import bms.player.beatoraja.play.TargetProperty;
import bms.player.beatoraja.song.*;
import javafx.application.Platform;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
-import javafx.scene.AccessibleAttribute;
import javafx.scene.Scene;
import javafx.scene.control.*;
-import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
@@ -264,6 +254,8 @@ public class PlayConfigurationView implements Initializable {
@FXML
private ComboBox httpDownloadSource;
@FXML
+ private CheckBox autoSwitchHttpDownloadSource;
+ @FXML
private TextField defaultDownloadURL;
@FXML
private TextField overrideDownloadURL;
@@ -350,7 +342,7 @@ public void initialize(URL arg0, ResourceBundle arg1) {
initComboBox(autosavereplay3, autosaves);
initComboBox(autosavereplay4, autosaves);
- httpDownloadSource.getItems().setAll(HttpDownloadProcessor.DOWNLOAD_SOURCES.keySet());
+ httpDownloadSource.getItems().setAll(HttpDownloadProcessor.DOWNLOAD_SOURCE_METAS.keySet());
notesdisplaytiming.setValueFactoryValues(PlayerConfig.JUDGETIMING_MIN, PlayerConfig.JUDGETIMING_MAX, 0, 1);
resourceController.init(this);
discordController.init(this);
@@ -483,6 +475,7 @@ public void update(Config config) {
enableHttp.setSelected(config.isEnableHttp());
httpDownloadSource.setValue(config.getDownloadSource());
+ autoSwitchHttpDownloadSource.setSelected(config.isAutoSwitchHttpDownloadSource());
defaultDownloadURL.setText(config.getDefaultDownloadURL());
overrideDownloadURL.setText(config.getOverrideDownloadURL());
@@ -631,6 +624,7 @@ public void commit() {
config.setEnableHttp(enableHttp.isSelected());
config.setDownloadSource(httpDownloadSource.getValue());
+ config.setAutoSwitchHttpDownloadSource(autoSwitchHttpDownloadSource.isSelected());
config.setOverrideDownloadURL(overrideDownloadURL.getText());
config.setClipboardWhenScreenshot(clipboardScreenshot.isSelected());
diff --git a/core/src/bms/tool/mdprocessor/HttpDownloadProcessor.java b/core/src/bms/tool/mdprocessor/HttpDownloadProcessor.java
index 35355d01..816e0bb8 100644
--- a/core/src/bms/tool/mdprocessor/HttpDownloadProcessor.java
+++ b/core/src/bms/tool/mdprocessor/HttpDownloadProcessor.java
@@ -1,26 +1,25 @@
package bms.tool.mdprocessor;
+import bms.player.beatoraja.Config;
import bms.player.beatoraja.MainController;
import bms.player.beatoraja.modmenu.ImGuiNotify;
-import com.badlogic.gdx.graphics.Color;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
+import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* In-game download processor. In charge of:
@@ -37,17 +36,18 @@
*/
public class HttpDownloadProcessor {
private static final Logger logger = LoggerFactory.getLogger(HttpDownloadProcessor.class);
- public static final Map DOWNLOAD_SOURCES = new HashMap<>();
+
+ public static final Map DOWNLOAD_SOURCE_METAS = new HashMap<>();
public static final int MAXIMUM_DOWNLOAD_COUNT = 5;
- private String downloadDirectory;
+ private final String downloadDirectory;
static {
- // Wriggle
+ // Wriggle (default download source)
HttpDownloadSourceMeta wriggleDownloadSourceMeta = WriggleDownloadSource.META;
- DOWNLOAD_SOURCES.put(wriggleDownloadSourceMeta.getName(), wriggleDownloadSourceMeta);
+ DOWNLOAD_SOURCE_METAS.put(wriggleDownloadSourceMeta.getName(), wriggleDownloadSourceMeta);
// Konmai
HttpDownloadSourceMeta konmaiDownloadSourceMeta = KonmaiDownloadSource.META;
- DOWNLOAD_SOURCES.put(konmaiDownloadSourceMeta.getName(), konmaiDownloadSourceMeta);
+ DOWNLOAD_SOURCE_METAS.put(konmaiDownloadSourceMeta.getName(), konmaiDownloadSourceMeta);
}
// id => task
@@ -59,12 +59,22 @@ public class HttpDownloadProcessor {
private final ExecutorService submitter = Executors.newSingleThreadExecutor();
// A reference to the main controller, only used for updating folder and rendering the message
private final MainController main;
- private final HttpDownloadSource httpDownloadSource;
+ private final HttpDownloadSource preferredDownloadSource;
+ private final boolean autoSwitchHttpDownloadSource;
+ // Download source instances
+ private Map DOWNLOAD_SOURCES = new HashMap<>();
- public HttpDownloadProcessor(MainController main, HttpDownloadSource httpDownloadSource, String downloadDirectory) {
+ public HttpDownloadProcessor(MainController main, Config config) {
this.main = main;
- this.httpDownloadSource = httpDownloadSource;
- this.downloadDirectory = downloadDirectory;
+ DOWNLOAD_SOURCES = DOWNLOAD_SOURCE_METAS.entrySet()
+ .stream()
+ .map(entry -> Pair.of(entry.getKey(), entry.getValue().build(config)))
+ .collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
+ this.preferredDownloadSource = DOWNLOAD_SOURCES.containsKey(config.getDownloadSource())
+ ? DOWNLOAD_SOURCES.get(config.getDownloadSource())
+ : DOWNLOAD_SOURCES.get(getDefaultDownloadSource().getName());
+ this.downloadDirectory = config.getDownloadDirectory();
+ this.autoSwitchHttpDownloadSource = config.isAutoSwitchHttpDownloadSource() && DOWNLOAD_SOURCE_METAS.size() != 1;
}
public static HttpDownloadSourceMeta getDefaultDownloadSource() {
@@ -80,21 +90,45 @@ private Optional getTaskById(int taskId) {
// however I'm not sure if that is possible in java
public Map getAllTasks() { return tasks; }
+ /**
+ * Submit a download task based on md5, and use the configured download source
+ */
+ public void submitMD5Task(String md5, String taskName) {
+ submitMD5Task(md5, taskName, this.preferredDownloadSource, new HashSet<>());
+ }
+
/**
* Submit a download task based on md5
*
* @param md5 missing sabun's md5
* @param taskName task name, normally sabun's name
+ * @param source specified download source, must be non-null
+ * @param triedSources the download sources that have already been tried
*/
- public void submitMD5Task(String md5, String taskName) {
- logger.info("[HttpDownloadProcessor] Trying to submit new download task[{}](based on md5: {})", taskName, md5);
- String sourceName = httpDownloadSource.getName();
+ public void submitMD5Task(String md5, String taskName, HttpDownloadSource source, Set triedSources) {
+ String sourceName = source.getName();
+ triedSources.add(sourceName);
+ logger.info("[HttpDownloadProcessor] Trying to submit new download task[{}](based on md5: {}, source: {})", taskName, md5, sourceName);
String downloadURL;
try {
- downloadURL = httpDownloadSource.getDownloadURLBasedOnMd5(md5);
+ downloadURL = source.getDownloadURLBasedOnMd5(md5);
} catch (FileNotFoundException e) {
logger.error("[HttpDownloadProcessor] Remote server[{}] reports no such data", sourceName);
ImGuiNotify.error(String.format("Cannot find specified song from %s", sourceName));
+ if (autoSwitchHttpDownloadSource) {
+ Optional any = DOWNLOAD_SOURCES.entrySet()
+ .stream()
+ .filter(entry -> !triedSources.contains(entry.getKey()))
+ .map(Map.Entry::getValue)
+ .findAny();
+ if (any.isPresent()) {
+ logger.info("[HttpDownloadProcessor] Trying to switch download source");
+ submitMD5Task(md5, taskName, any.get(), triedSources);
+ } else {
+ logger.info("[HttpDownloadProcessor] All download sources doesn't have this chart");
+ ImGuiNotify.error("All download sources doesn't have this chart");
+ }
+ }
return;
} catch (RuntimeException e) {
e.printStackTrace();
@@ -163,7 +197,7 @@ public void executeDownloadTask(DownloadTask downloadTask) {
result = downloadFileFromURL(downloadTask, String.format("%s.7z", hash));
} catch (Exception e) {
e.printStackTrace();
- ImGuiNotify.error(String.format("Failed downloading from %s due to %s", httpDownloadSource.getName(), e.getMessage()));
+ ImGuiNotify.error(String.format("Failed downloading from %s due to %s", preferredDownloadSource.getName(), e.getMessage()));
}
if (result == null) {
// Download failed, skip the remaining steps
@@ -224,7 +258,7 @@ private Path downloadFileFromURL(DownloadTask task, String fallbackFileName) {
int responseCode = conn.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
- throw new FileNotFoundException("Package not found at " + httpDownloadSource.getName());
+ throw new FileNotFoundException("Package not found at " + preferredDownloadSource.getName());
}
throw new IllegalStateException("Unexpected http response code: " + responseCode);
}
diff --git a/core/src/resources/UIResources.properties b/core/src/resources/UIResources.properties
index 0f753d91..19eeccad 100644
--- a/core/src/resources/UIResources.properties
+++ b/core/src/resources/UIResources.properties
@@ -150,6 +150,7 @@ TWITTER_CONSUMER_KEY=Consumer Key
TWITTER_CONSUMER_SECRET=Consumer Secret
ENABLE_IPFS=Enable automatic download of BMS by IPFS
ENABLE_HTTP=Enable automatic download of BMS by HTTP
+AUTO_SWITCH_HTTP_DOWNLOAD_SOURCE=Auto switch download source when package is missing
MOUSE_SCRATCH=Mouse Scratch
MOUSE_SCRATCH_TIME_THRESHOLD=Mouse Scratch Threshold (ms)
MOUSE_SCRATCH_DISTANCE=Mouse Scratch Distance