Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion core/src/bms/player/beatoraja/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
3 changes: 1 addition & 2 deletions core/src/bms/player/beatoraja/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@

<CheckBox fx:id="enableHttp" mnemonicParsing="false" prefHeight="18.0" text="%ENABLE_HTTP" />
<ComboBox fx:id="httpDownloadSource" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<CheckBox fx:id="autoSwitchHttpDownloadSource" mnemonicParsing="false" prefHeight="18.0" text="%AUTO_SWITCH_HTTP_DOWNLOAD_SOURCE" />
<GridPane hgap="10" vgap="5">
<children>
<Label text="Default Http Server URL" prefHeight="25.0" GridPane.rowIndex="0" GridPane.columnIndex="0" />
Expand Down
16 changes: 5 additions & 11 deletions core/src/bms/player/beatoraja/launcher/PlayConfigurationView.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -264,6 +254,8 @@ public class PlayConfigurationView implements Initializable {
@FXML
private ComboBox<String> httpDownloadSource;
@FXML
private CheckBox autoSwitchHttpDownloadSource;
@FXML
private TextField defaultDownloadURL;
@FXML
private TextField overrideDownloadURL;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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());

Expand Down Expand Up @@ -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());
Expand Down
35 changes: 35 additions & 0 deletions core/src/bms/tool/mdprocessor/DownloadRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package bms.tool.mdprocessor;

import java.util.HashSet;
import java.util.Set;

/**
* DownloadRequest is a simple data object that records the context data during the process of downloading a package.
*/
public class DownloadRequest {
private String md5;
private String name;
private Set<String> triedSources;

public DownloadRequest(String md5, String name) {
this.md5 = md5;
this.name = name;
this.triedSources = new HashSet<>();
}

public void markTried(HttpDownloadSource downloadSource) {
this.triedSources.add(downloadSource.getName());
}

public boolean hasTried(String downloadSource) {
return this.triedSources.contains(downloadSource);
}

public String getMd5() {
return md5;
}

public String getName() {
return name;
}
}
12 changes: 5 additions & 7 deletions core/src/bms/tool/mdprocessor/DownloadTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@
public class DownloadTask {
final private int id;
final private String url;
final private String name;
final private String hash;

private DownloadRequest request;
private DownloadTaskStatus downloadTaskStatus;
private long downloadSize;
private long contentLength;
private String errorMessage;
private volatile long timeFinished;

public DownloadTask(int id, String url, String name, String hash) {
public DownloadTask(int id, String url, DownloadRequest request) {
this.id = id;
this.url = url;
this.name = name;
this.hash = hash;
this.request = request;
this.downloadTaskStatus = DownloadTaskStatus.Prepare;
}

Expand All @@ -31,7 +29,7 @@ public String getUrl() {
}

public String getHash() {
return hash;
return request.getMd5();
}

public DownloadTaskStatus getDownloadTaskStatus() {
Expand Down Expand Up @@ -70,7 +68,7 @@ public void setErrorMessage(String errorMessage) {
}

public String getName() {
return name;
return request.getName();
}

public long getTimeFinished() {
Expand Down
90 changes: 63 additions & 27 deletions core/src/bms/tool/mdprocessor/HttpDownloadProcessor.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
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.nio.file.Paths;
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:
Expand All @@ -38,20 +37,21 @@
*/
public class HttpDownloadProcessor {
private static final Logger logger = LoggerFactory.getLogger(HttpDownloadProcessor.class);
public static final Map<String, HttpDownloadSourceMeta> DOWNLOAD_SOURCES = new HashMap<>();

public static final Map<String, HttpDownloadSourceMeta> DOWNLOAD_SOURCE_METAS = new HashMap<>();
public static final int MAXIMUM_DOWNLOAD_COUNT = 5;
private String downloadDirectory;
private final String downloadDirectory;

static {
// Ginger
HttpDownloadSourceMeta gingerDownloadSourceMeta = GingerDownloadSource.META;
DOWNLOAD_SOURCES.put(gingerDownloadSourceMeta.getName(), gingerDownloadSourceMeta);
DOWNLOAD_SOURCE_METAS.put(gingerDownloadSourceMeta.getName(), gingerDownloadSourceMeta);
// Wriggle
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
Expand All @@ -63,12 +63,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<String, HttpDownloadSource> 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() {
Expand All @@ -85,20 +95,46 @@ private Optional<DownloadTask> getTaskById(int taskId) {
public Map<Integer, DownloadTask> getAllTasks() { return tasks; }

/**
* Submit a download task based on md5
*
* @param md5 missing sabun's md5
* @param taskName task name, normally sabun's name
* Submit a download request based on md5, and use the configured download source
*/
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();
DownloadRequest req = new DownloadRequest(md5, taskName);
submitMD5Task(req, this.preferredDownloadSource);
}

/**
* Try submitting a download request with a specified download source
*
* @param request download request
* @param source specified download source, must be non-null
*/
public void submitMD5Task(DownloadRequest request, HttpDownloadSource source) {
String sourceName = source.getName();
request.markTried(source);
logger.info(
"[HttpDownloadProcessor] Trying to submit new download task[{}](based on md5: {}, source: {})",
request.getName(), request.getMd5(), sourceName
);
String downloadURL;
try {
downloadURL = httpDownloadSource.getDownloadURLBasedOnMd5(md5);
downloadURL = source.getDownloadURLBasedOnMd5(request.getMd5());
} 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<HttpDownloadSource> any = DOWNLOAD_SOURCES.entrySet()
.stream()
.filter(entry -> !request.hasTried(entry.getKey()))
.map(Map.Entry::getValue)
.findAny();
if (any.isPresent()) {
logger.info("[HttpDownloadProcessor] Trying another download source: {}", any.get().getName());
submitMD5Task(request, any.get());
} 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();
Expand All @@ -120,9 +156,9 @@ public void submitMD5Task(String md5, String taskName) {
return null;
}
int taskId = idGenerator.addAndGet(1);
DownloadTask downloadTask = new DownloadTask(taskId, downloadURL, taskName, md5);
DownloadTask downloadTask = new DownloadTask(taskId, downloadURL, request);
tasks.put(taskId, downloadTask);
ImGuiNotify.info(String.format("New download task[%s] submitted", taskName));
ImGuiNotify.info(String.format("New download task[%s] submitted", downloadTask.getName()));
return downloadTask;
}
});
Expand Down Expand Up @@ -167,7 +203,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
Expand Down Expand Up @@ -229,7 +265,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);
}
Expand Down
1 change: 1 addition & 0 deletions core/src/resources/UIResources.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down