solution for Issue 643: Integrate top decks json feed from Firemind.
parent
0ba16d4894
commit
21b57a7267
|
@ -31,6 +31,8 @@ release/Magarena/logs
|
|||
notes/
|
||||
release/Magarena/players
|
||||
release/Magarena/decks/*.dec
|
||||
release/Magarena/decks/firemind
|
||||
release/Magarena/firemind
|
||||
syntax: regexp
|
||||
release/Magarena-debug.sh
|
||||
release/Magarena-debug-jar.bat
|
||||
|
|
|
@ -16,10 +16,11 @@ public enum DeckType {
|
|||
// TODO: Recent("Recently Played"), // last 20 most recently played decks
|
||||
Random("Random"),
|
||||
Preconstructed("Prebuilt"),
|
||||
Custom("Player")
|
||||
Custom("Player"),
|
||||
Firemind("Firemind Top Decks")
|
||||
;
|
||||
|
||||
public static final Set<DeckType> PREDEFINED_DECKS = EnumSet.range(Preconstructed, Custom);
|
||||
public static final Set<DeckType> PREDEFINED_DECKS = EnumSet.range(Preconstructed, Firemind);
|
||||
|
||||
private final String deckTypeCaption;
|
||||
|
||||
|
|
|
@ -97,15 +97,15 @@ public class DeckUtils {
|
|||
}
|
||||
final String description = deck.getDescription();
|
||||
if (description != null) {
|
||||
writer.write(">" + description);
|
||||
writer.write(">" + description.replaceAll("(\\r|\\n|\\r\\n)", "\\\\n"));
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
isSuccessful = false;
|
||||
System.err.println("ERROR! Unable to save deck");
|
||||
System.err.println(ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
System.err.println("Invalid deck : " + deck.getFilename() + " - " + ex.getMessage());
|
||||
} finally {
|
||||
magic.data.FileIO.close(writer);
|
||||
if (writer != null) {
|
||||
magic.data.FileIO.close(writer);
|
||||
}
|
||||
}
|
||||
|
||||
return isSuccessful;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package magic.firemind;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import magic.data.DownloadableFile;
|
||||
|
||||
public class FiremindJsonFile extends DownloadableFile {
|
||||
|
||||
private static final String JSON_URL = "https://www.firemind.ch/decks/top.json";
|
||||
|
||||
private final URL url;
|
||||
private final File file;
|
||||
|
||||
public FiremindJsonFile(final File jsonFile) throws MalformedURLException {
|
||||
url = new URL(JSON_URL);
|
||||
file = jsonFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Proxy proxy) throws IOException {
|
||||
final File tempFile = new File(file.getParent(), "~" + file.getName());
|
||||
DownloadableFile.downloadToFile(proxy, url, tempFile);
|
||||
Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getDownloadUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package magic.firemind;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import magic.data.DeckUtils;
|
||||
import magic.data.GeneralConfig;
|
||||
import magic.model.MagicDeck;
|
||||
import magic.utility.MagicFileSystem;
|
||||
import magic.utility.MagicFileSystem.DataPath;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
public final class FiremindJsonReader {
|
||||
private FiremindJsonReader() {}
|
||||
|
||||
private static boolean isJsonFileUpToDate(final File jsonFile) {
|
||||
final Calendar calToday = Calendar.getInstance();
|
||||
final Calendar calFile = Calendar.getInstance();
|
||||
calFile.setTimeInMillis(jsonFile.lastModified());
|
||||
return calToday.get(Calendar.YEAR) == calFile.get(Calendar.YEAR)
|
||||
&& calToday.get(Calendar.MONTH) == calFile.get(Calendar.MONTH)
|
||||
&& calToday.get(Calendar.DAY_OF_MONTH) == calFile.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
private static void downloadLatestJsonFile(final File jsonFile) {
|
||||
try {
|
||||
final FiremindJsonFile downloadFile = new FiremindJsonFile(jsonFile);
|
||||
downloadFile.download(GeneralConfig.getInstance().getProxy());
|
||||
} catch (IOException ex) {
|
||||
System.err.println("Download of json file failed : " + ex.getMessage());
|
||||
}
|
||||
if (jsonFile.exists()) {
|
||||
if (!isJsonFileUpToDate(jsonFile)) {
|
||||
// only attempt download once per day even if download fails.
|
||||
final Calendar cal = Calendar.getInstance();
|
||||
jsonFile.setLastModified(cal.getTimeInMillis());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// a problem occurred so create empty json file so that for
|
||||
// the rest of the day does not keep trying to download.
|
||||
jsonFile.createNewFile();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearFiremindDecksDirectory(final Path firemindDecksPath) throws IOException {
|
||||
FileUtils.deleteDirectory(firemindDecksPath.toFile());
|
||||
MagicFileSystem.verifyDirectoryPath(firemindDecksPath);
|
||||
}
|
||||
|
||||
public static void refreshTopDecks() throws IOException {
|
||||
|
||||
final File jsonFile = MagicFileSystem.getDataPath(DataPath.FIREMIND).resolve("topdecks.json").toFile();
|
||||
|
||||
if (jsonFile.exists() && isJsonFileUpToDate(jsonFile)) {
|
||||
// JSON file has already been downloaded/updated today.
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure Firemind decks directory exists.
|
||||
final Path firemindDecksPath = MagicFileSystem.getDataPath(DataPath.DECKS).resolve("firemind");
|
||||
MagicFileSystem.verifyDirectoryPath(firemindDecksPath);
|
||||
|
||||
downloadLatestJsonFile(jsonFile);
|
||||
if (jsonFile.length() == 0) {
|
||||
// a problem occurred, use existing decks.
|
||||
return;
|
||||
}
|
||||
|
||||
final List<MagicDeck> decks = JsonOrgParser.parse(jsonFile);
|
||||
|
||||
clearFiremindDecksDirectory(firemindDecksPath);
|
||||
for (MagicDeck deck : decks) {
|
||||
try {
|
||||
final String filename = firemindDecksPath.resolve(deck.getFilename() + ".dec").toString();
|
||||
DeckUtils.saveDeck(filename, deck);
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Invalid deck : " + deck.getFilename() + " - " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package magic.firemind;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import magic.data.DeckUtils;
|
||||
import magic.model.MagicCardDefinition;
|
||||
import magic.model.MagicDeck;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public final class JsonOrgParser {
|
||||
private JsonOrgParser() {}
|
||||
|
||||
public static List<MagicDeck> parse(final File jsonFile) throws IOException {
|
||||
|
||||
final List<MagicDeck> decks = new ArrayList<>();
|
||||
|
||||
final JSONObject jsonRoot = new JSONObject(getJsonString(jsonFile));
|
||||
final List<String> formats = new ArrayList<>(Arrays.asList(JSONObject.getNames(jsonRoot)));
|
||||
for (String format : formats) {
|
||||
final JSONObject jsonFormat = jsonRoot.getJSONObject(format);
|
||||
final List<String> deckNames = new ArrayList<>(Arrays.asList(JSONObject.getNames(jsonFormat)));
|
||||
for (String deckName : deckNames) {
|
||||
final JSONObject jsonDeck = jsonFormat.getJSONObject(deckName);
|
||||
final MagicDeck deck = new MagicDeck();
|
||||
decks.add(deck);
|
||||
deck.setFilename(format + "." + deckName);
|
||||
deck.setDescription(getDeckDescription(jsonDeck));
|
||||
addCardsToDeck(deck, jsonDeck.getJSONArray("cards"));
|
||||
}
|
||||
}
|
||||
|
||||
return decks;
|
||||
}
|
||||
|
||||
private static void addCardsToDeck(final MagicDeck deck, final JSONArray jsonCards) {
|
||||
for (int i = 0; i < jsonCards.length(); i++) {
|
||||
final JSONObject jsonCard = jsonCards.getJSONObject(i);
|
||||
final String cardName = jsonCard.getString("name");
|
||||
final int cardQuantity = jsonCard.getInt("quantity");
|
||||
final MagicCardDefinition cardDef = DeckUtils.getCard(cardName);
|
||||
for (int j = 0; j < cardQuantity; j++) {
|
||||
deck.add(cardDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDeckDescription(final JSONObject jsonDeck) {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
sb.append("Author: ").append(jsonDeck.getString("author"));
|
||||
sb.append("\nRating: ").append(jsonDeck.getString("rating"));
|
||||
sb.append("\nReleased: ").append(getFormattedReleaseDate(jsonDeck.getString("releaseDate")));
|
||||
if (!jsonDeck.getString("description").trim().isEmpty()) {
|
||||
sb.append("\n\n").append(jsonDeck.getString("description"));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String getFormattedReleaseDate(final String jsonValue) {
|
||||
if (jsonValue.length() != 8) {
|
||||
return jsonValue;
|
||||
} else {
|
||||
return jsonValue.substring(0, 4) + "-"
|
||||
+ jsonValue.substring(4, 6) + "-"
|
||||
+ jsonValue.substring(6, 8);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getJsonString(final File jsonFile) throws IOException {
|
||||
try (final BufferedReader br = new BufferedReader(new FileReader(jsonFile))) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line = br.readLine();
|
||||
while (line != null) {
|
||||
sb.append(line);
|
||||
sb.append("\n");
|
||||
line = br.readLine();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -90,14 +90,14 @@ public class DeckDescriptionViewer extends JPanel implements FocusListener {
|
|||
}
|
||||
|
||||
public void setDeckDescription(String text) {
|
||||
textArea.setText(text == null || text.isEmpty() ? "" : text);
|
||||
textArea.setText(text == null || text.isEmpty() ? "" : text.replaceAll("\\\\n", "\n"));
|
||||
textArea.setCaretPosition(0);
|
||||
}
|
||||
|
||||
public void setDeckDescription(final MagicDeck deck) {
|
||||
if (deck != null) {
|
||||
final String text = deck.getDescription();
|
||||
textArea.setText(text == null || text.isEmpty() ? "" : text);
|
||||
textArea.setText(text == null || text.isEmpty() ? "" : text.replaceAll("\\\\n", "\n"));
|
||||
textArea.setCaretPosition(0);
|
||||
textArea.setForeground(deck.isValid() ? Color.BLACK : Color.RED.darker());
|
||||
} else {
|
||||
|
|
|
@ -30,6 +30,7 @@ import javax.swing.event.ListSelectionListener;
|
|||
import magic.MagicMain;
|
||||
import magic.data.DeckType;
|
||||
import magic.data.DeckUtils;
|
||||
import magic.firemind.FiremindJsonReader;
|
||||
import magic.model.MagicDeck;
|
||||
import magic.ui.dialog.DecksFilterDialog;
|
||||
import magic.ui.screen.interfaces.IDeckConsumer;
|
||||
|
@ -140,14 +141,17 @@ public class DeckPicker extends JPanel {
|
|||
final DeckType deckTypes[] = DeckType.PREDEFINED_DECKS.toArray(new DeckType[DeckType.PREDEFINED_DECKS.size()]);
|
||||
deckTypeJCombo.setModel(new DefaultComboBoxModel<>(deckTypes));
|
||||
deckTypeJCombo.setSelectedIndex(0);
|
||||
// deck names list
|
||||
refreshDecksList();
|
||||
}
|
||||
|
||||
private void refreshDecksList() {
|
||||
// ignore list selection events while setting list data.
|
||||
decksJList.removeListSelectionListener(listSelectionListener);
|
||||
decksJList.setListData(getDecksListData());
|
||||
try {
|
||||
decksJList.setListData(getDecksListData());
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
decksJList.addListSelectionListener(listSelectionListener);
|
||||
if (decksJList.getModel().getSize() > 0) {
|
||||
decksJList.setSelectedIndex(0);
|
||||
|
@ -160,15 +164,18 @@ public class DeckPicker extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
private MagicDeck[] getDecksListData() {
|
||||
private MagicDeck[] getDecksListData() throws IOException {
|
||||
switch (selectedDeckType) {
|
||||
case Preconstructed:
|
||||
return getFilteredDecksListData(DeckUtils.getPrebuiltDecksFolder());
|
||||
case Custom:
|
||||
return getFilteredDecksListData(Paths.get(DeckUtils.getDeckFolder()));
|
||||
case Firemind:
|
||||
FiremindJsonReader.refreshTopDecks();
|
||||
return getFilteredDecksListData(Paths.get(DeckUtils.getDeckFolder()).resolve("firemind"));
|
||||
default:
|
||||
return new MagicDeck[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MagicDeck[] getFilteredDecksListData(final Path decksPath) {
|
||||
|
@ -241,6 +248,8 @@ public class DeckPicker extends JPanel {
|
|||
for (IDeckConsumer listener : listeners) {
|
||||
if (selectedDeckType == DeckType.Random) {
|
||||
listener.setDeck(deck.getName(), selectedDeckType);
|
||||
} else if (selectedDeckType == DeckType.Firemind) {
|
||||
listener.setDeck(deck, getDeckPath(deck.getName(), selectedDeckType));
|
||||
} else {
|
||||
listener.setDeck(deck, getDeckPath(deck.getName(), selectedDeckType));
|
||||
}
|
||||
|
@ -256,6 +265,8 @@ public class DeckPicker extends JPanel {
|
|||
return DeckUtils.getPrebuiltDecksFolder().resolve(deckName + ".dec");
|
||||
case Custom:
|
||||
return Paths.get(DeckUtils.getDeckFolder()).resolve(deckName + ".dec");
|
||||
case Firemind:
|
||||
return Paths.get(DeckUtils.getDeckFolder()).resolve("firemind").resolve(deckName + ".dec");
|
||||
default:
|
||||
throw new RuntimeException("getDeckPath() not implemented for decktype: " + deckType);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ public final class MagicFileSystem {
|
|||
LOGS("logs"),
|
||||
DUELS("duels"),
|
||||
PLAYERS("players"),
|
||||
AVATARS("avatars");
|
||||
AVATARS("avatars"),
|
||||
FIREMIND("firemind");
|
||||
|
||||
private final Path directoryPath;
|
||||
|
||||
|
|
Loading…
Reference in New Issue