wörk wörk (day 2)

This commit is contained in:
Niklas 2020-03-27 03:04:15 +01:00
parent 2decbf015c
commit ed51a990a9
13 changed files with 856 additions and 236 deletions

16
pom.xml
View File

@ -31,6 +31,11 @@
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<version>2.6</version> <version>2.6</version>
</dependency> </dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
<dependency> <dependency>
<groupId>com.formdev</groupId> <groupId>com.formdev</groupId>
<artifactId>flatlaf</artifactId> <artifactId>flatlaf</artifactId>
@ -47,15 +52,14 @@
<version>0.5.4</version> <version>0.5.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.takari.zsync</groupId> <groupId>com.github.mfornos</groupId>
<artifactId>zsync-parent</artifactId> <artifactId>humanize-slim</artifactId>
<version>0.1.0</version> <version>1.2.2</version>
<type>pom</type>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.bitshifted</groupId> <groupId>com.github.Gurkengewuerz</groupId>
<artifactId>zsyncer</artifactId> <artifactId>zsyncer</artifactId>
<version>-f69d844481-1</version> <version>locale_fix-2f7565d392-1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.squareup.okhttp3</groupId> <groupId>com.squareup.okhttp3</groupId>

View File

@ -3,7 +3,7 @@
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/> <margin top="0" left="0" bottom="0" right="0"/>
<constraints> <constraints>
<xy x="20" y="20" width="1048" height="526"/> <xy x="20" y="20" width="1048" height="556"/>
</constraints> </constraints>
<properties/> <properties/>
<border type="none"/> <border type="none"/>
@ -407,7 +407,7 @@
</component> </component>
</children> </children>
</grid> </grid>
<grid id="3b45c" layout-manager="GridLayoutManager" row-count="6" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <grid id="3b45c" layout-manager="GridLayoutManager" row-count="6" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="3" bottom="0" right="0"/> <margin top="0" left="3" bottom="0" right="0"/>
<constraints border-constraint="Center"/> <constraints border-constraint="Center"/>
<properties/> <properties/>
@ -423,7 +423,7 @@
</component> </component>
<hspacer id="f47e0"> <hspacer id="f47e0">
<constraints> <constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> <grid row="0" column="2" row-span="1" col-span="2" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
</hspacer> </hspacer>
<component id="7b453" class="javax.swing.JLabel" binding="syncCheckStatusLabel"> <component id="7b453" class="javax.swing.JLabel" binding="syncCheckStatusLabel">
@ -437,7 +437,7 @@
</component> </component>
<component id="c7b6" class="javax.swing.JProgressBar" binding="syncCheckProgress"> <component id="c7b6" class="javax.swing.JProgressBar" binding="syncCheckProgress">
<constraints> <constraints>
<grid row="1" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> <grid row="1" column="0" row-span="1" col-span="4" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties> <properties>
<string value=""/> <string value=""/>
@ -448,7 +448,7 @@
<grid id="c1d6e" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <grid id="c1d6e" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/> <margin top="0" left="0" bottom="0" right="0"/>
<constraints> <constraints>
<grid row="2" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> <grid row="2" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties/> <properties/>
<border type="none"/> <border type="none"/>
@ -526,6 +526,23 @@
<text value="0"/> <text value="0"/>
</properties> </properties>
</component> </component>
<component id="4c8b" class="javax.swing.JLabel">
<constraints>
<grid row="3" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="lang" key="changed_filesize"/>
</properties>
</component>
<component id="585a0" class="javax.swing.JLabel" binding="syncChangedFileSizeLabel">
<constraints>
<grid row="3" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="0.0 B"/>
<toolTipText resource-bundle="lang" key="changed_filesize_tooltip"/>
</properties>
</component>
</children> </children>
</grid> </grid>
</children> </children>
@ -573,22 +590,9 @@
<text resource-bundle="lang" key="total_file_size"/> <text resource-bundle="lang" key="total_file_size"/>
</properties> </properties>
</component> </component>
<vspacer id="54eb4">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="d85a8" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="lang" key="downloaded"/>
</properties>
</component>
<component id="f5997" class="javax.swing.JLabel"> <component id="f5997" class="javax.swing.JLabel">
<constraints> <constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties> <properties>
<text resource-bundle="lang" key="speed"/> <text resource-bundle="lang" key="speed"/>
@ -596,7 +600,7 @@
</component> </component>
<component id="25551" class="javax.swing.JLabel"> <component id="25551" class="javax.swing.JLabel">
<constraints> <constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties> <properties>
<text resource-bundle="lang" key="remaining_time"/> <text resource-bundle="lang" key="remaining_time"/>
@ -610,17 +614,9 @@
<text value="0.0 B"/> <text value="0.0 B"/>
</properties> </properties>
</component> </component>
<component id="d9013" class="javax.swing.JLabel"> <component id="617c7" class="javax.swing.JLabel" binding="syncDownloadSpeedLabel">
<constraints> <constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="617c7" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties> <properties>
<text value=""/> <text value=""/>
@ -628,7 +624,39 @@
</component> </component>
<component id="b64ac" class="javax.swing.JLabel"> <component id="b64ac" class="javax.swing.JLabel">
<constraints> <constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> <grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="d85a8" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="lang" key="downloaded"/>
</properties>
</component>
<component id="d9013" class="javax.swing.JLabel" binding="syncDownloadedLabel">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="28be3" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="lang" key="file_count"/>
</properties>
</component>
<component id="9148" class="javax.swing.JLabel" binding="syncFileCountLabel">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties> <properties>
<text value=""/> <text value=""/>
@ -671,24 +699,24 @@
<properties/> <properties/>
<border type="none"/> <border type="none"/>
<children> <children>
<component id="f39cc" class="javax.swing.JProgressBar" binding="progressBar2" default-binding="true"> <component id="f39cc" class="javax.swing.JProgressBar" binding="syncDownloadProgress">
<constraints> <constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties> <properties>
<string value=""/> <string value=""/>
<stringPainted value="true"/> <stringPainted value="true"/>
<value value="50"/> <value value="0"/>
</properties> </properties>
</component> </component>
<component id="87392" class="javax.swing.JProgressBar" binding="progressBar3" default-binding="true"> <component id="87392" class="javax.swing.JProgressBar" binding="syncFileProgress">
<constraints> <constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints> </constraints>
<properties> <properties>
<string value=""/> <string value=""/>
<stringPainted value="true"/> <stringPainted value="true"/>
<value value="50"/> <value value="0"/>
</properties> </properties>
</component> </component>
<grid id="99173" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <grid id="99173" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">

View File

@ -5,6 +5,7 @@ import de.mc8051.arma3launcher.model.JCheckBoxTree;
import de.mc8051.arma3launcher.model.ModListRenderer; import de.mc8051.arma3launcher.model.ModListRenderer;
import de.mc8051.arma3launcher.model.PresetListRenderer; import de.mc8051.arma3launcher.model.PresetListRenderer;
import de.mc8051.arma3launcher.model.PresetTableModel; import de.mc8051.arma3launcher.model.PresetTableModel;
import de.mc8051.arma3launcher.model.RepositoryTreeNode;
import de.mc8051.arma3launcher.model.ServerTableModel; import de.mc8051.arma3launcher.model.ServerTableModel;
import de.mc8051.arma3launcher.objects.AbstractMod; import de.mc8051.arma3launcher.objects.AbstractMod;
import de.mc8051.arma3launcher.objects.Mod; import de.mc8051.arma3launcher.objects.Mod;
@ -13,9 +14,12 @@ import de.mc8051.arma3launcher.objects.Modset;
import de.mc8051.arma3launcher.objects.Server; import de.mc8051.arma3launcher.objects.Server;
import de.mc8051.arma3launcher.repo.FileChecker; import de.mc8051.arma3launcher.repo.FileChecker;
import de.mc8051.arma3launcher.repo.RepositoryManger; import de.mc8051.arma3launcher.repo.RepositoryManger;
import de.mc8051.arma3launcher.repo.SyncList;
import de.mc8051.arma3launcher.repo.Syncer;
import de.mc8051.arma3launcher.steam.SteamTimer; import de.mc8051.arma3launcher.steam.SteamTimer;
import de.mc8051.arma3launcher.utils.Callback; import de.mc8051.arma3launcher.utils.Callback;
import de.mc8051.arma3launcher.utils.LangUtils; import de.mc8051.arma3launcher.utils.LangUtils;
import humanize.Humanize;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
@ -24,6 +28,7 @@ import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.DefaultFormatter; import javax.swing.text.DefaultFormatter;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
@ -37,11 +42,6 @@ import java.net.URLDecoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -115,8 +115,8 @@ public class LauncherGUI implements Observer {
private JProgressBar syncCheckProgress; private JProgressBar syncCheckProgress;
private JButton syncCheckAbortButton; private JButton syncCheckAbortButton;
private JButton syncCheckButton; private JButton syncCheckButton;
private JProgressBar progressBar2; public JProgressBar syncDownloadProgress;
private JProgressBar progressBar3; public JProgressBar syncFileProgress;
private JButton syncDownloadButton; private JButton syncDownloadButton;
private JButton syncDownloadAbortButton; private JButton syncDownloadAbortButton;
private JButton syncPauseButton; private JButton syncPauseButton;
@ -130,29 +130,33 @@ public class LauncherGUI implements Observer {
private JLabel syncAddedFilesLabel; private JLabel syncAddedFilesLabel;
private JLabel syncChangedFilesLabel; private JLabel syncChangedFilesLabel;
private JLabel syncSizeLabel; private JLabel syncSizeLabel;
private JLabel syncChangedFileSizeLabel;
private JLabel syncFileCountLabel;
public JLabel syncDownloadedLabel;
public JLabel syncDownloadSpeedLabel;
private JCheckBoxTree repoTree; private JCheckBoxTree repoTree;
private FileChecker fileChecker; private FileChecker fileChecker;
private Syncer syncer;
// TODO: Updater private SyncList lastSynclist;
/*
Prüfung
In eine Liste hinzufügen wenn Datei in modset.json (Neu runterladen), nicht in modset.json (zum Löschen) oder die Größe unterschiedlich ist (Geändert)
Checkboxen beim Syncronisieren deaktivieren
*/
public LauncherGUI() { public LauncherGUI() {
fileChecker = new FileChecker(syncCheckProgress); fileChecker = new FileChecker(syncCheckProgress);
syncer = new Syncer(this);
RepositoryManger.getInstance().addObserver(this); RepositoryManger.getInstance().addObserver(this);
SteamTimer.addObserver(this); SteamTimer.addObserver(this);
fileChecker.addObserver(this); fileChecker.addObserver(this);
syncer.addObserver(this);
updateTreePanel.remove(tree1); updateTreePanel.remove(tree1);
repoTree = new JCheckBoxTree(); repoTree = new JCheckBoxTree();
updateTreePanel.add(repoTree, BorderLayout.CENTER); updateTreePanel.add(repoTree, BorderLayout.CENTER);
DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel();
model.setRoot(new RepositoryTreeNode("Repository"));
updateTreePanel.revalidate(); updateTreePanel.revalidate();
updateTreePanel.repaint(); updateTreePanel.repaint();
@ -221,7 +225,6 @@ public class LauncherGUI implements Observer {
if (!e.getValueIsAdjusting()) { if (!e.getValueIsAdjusting()) {
PresetTableModel m = (PresetTableModel) presetList.getModel(); PresetTableModel m = (PresetTableModel) presetList.getModel();
Modset modset = (Modset) m.getElementAt(presetList.getSelectedIndex()); Modset modset = (Modset) m.getElementAt(presetList.getSelectedIndex());
System.out.println(modset.getName());
if (modset.getType() == Modset.Type.SERVER) { if (modset.getType() == Modset.Type.SERVER) {
renamePresetButton.setEnabled(false); renamePresetButton.setEnabled(false);
@ -283,6 +286,11 @@ public class LauncherGUI implements Observer {
new Thread(() -> { new Thread(() -> {
RepositoryManger.getInstance().refreshMeta(); RepositoryManger.getInstance().refreshMeta();
try {
Thread.sleep(750);
} catch (InterruptedException e) {
e.printStackTrace();
}
RepositoryManger.getInstance().refreshModset(); RepositoryManger.getInstance().refreshModset();
}).start(); }).start();
@ -294,7 +302,8 @@ public class LauncherGUI implements Observer {
syncCheckStatusLabel.setText("Running!"); syncCheckStatusLabel.setText("Running!");
new Thread(() -> fileChecker.check()).start(); new Thread(() -> fileChecker.check()).start();
// TODO: disable JTree Checkboxes repoTree.setCheckboxesEnabled(false);
repoTree.setCheckboxesChecked(false);
} }
}); });
@ -304,6 +313,28 @@ public class LauncherGUI implements Observer {
fileChecker.stop(); fileChecker.stop();
} }
}); });
syncDownloadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new Thread(() -> syncer.sync(lastSynclist.clone())).start();
}
});
syncDownloadAbortButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
syncer.stop();
}
});
syncPauseButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
syncer.setPaused(!syncer.isPaused());
syncPauseButton.setEnabled(false);
}
});
} }
public static void infoBox(String infoMessage, String titleBar) { public static void infoBox(String infoMessage, String titleBar) {
@ -550,78 +581,54 @@ public class LauncherGUI implements Observer {
spinner.addChangeListener(new SettingsHandler.SpinnerListener(paraObj)); spinner.addChangeListener(new SettingsHandler.SpinnerListener(paraObj));
} }
public ArrayList<AbstractMod> getSyncList() { public SyncList getSyncList() {
ArrayList<AbstractMod> modList = new ArrayList<>(); SyncList synclist = new SyncList();
HashMap<String, ArrayList<String>> tempMap = new HashMap<>(); DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel();
for (TreePath checkedPath : repoTree.getCheckedPaths()) { RepositoryTreeNode root = (RepositoryTreeNode) model.getRoot();
DefaultMutableTreeNode tn = (DefaultMutableTreeNode)checkedPath.getLastPathComponent(); for (TreeNode leaf : root.getAllLeafNodes()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) leaf;
TreeNode[] path = node.getPath();
boolean isSelected = repoTree.isSelected(new TreePath(path));
if (!isSelected) continue;
if(tn.getChildCount() > 0) continue; ArrayList<String> treePathList = new ArrayList<>();
Object[] path = checkedPath.getPath(); for (int i = 2; i < path.length; i++) {
DefaultMutableTreeNode[] modifiedArray = Arrays.stream(Arrays.copyOfRange(path, 1, path.length)).toArray(DefaultMutableTreeNode[]::new); treePathList.add(String.valueOf(((DefaultMutableTreeNode) path[i]).getUserObject()));
ArrayList<String> strings = new ArrayList<>();
if(tempMap.containsKey(String.valueOf(modifiedArray[0].getUserObject()))) {
strings = tempMap.get(String.valueOf(modifiedArray[0].getUserObject()));
} }
String treePath = String.join("/", treePathList);
String modname = String.valueOf(((DefaultMutableTreeNode) path[1]).getUserObject());
String modPath = ""; if (fileChecker.getChanged().containsKey(modname)) {
for (int i = 1; i < modifiedArray.length; i++) { for (ModFile modFile : fileChecker.getChanged().get(modname)) {
modPath += String.valueOf(modifiedArray[i].getUserObject()) + "/"; if (String.join("/", modFile.getPath()).equals(treePath)) {
} synclist.add(modFile);
modPath = modPath.isEmpty() ? "" : modPath.substring(0, modPath.length() - 1);
strings.add(modPath);
tempMap.put((String) modifiedArray[0].getUserObject(), strings);
}
for (Map.Entry<String, ArrayList<String>> entry : tempMap.entrySet()) {
String modS = entry.getKey();
ArrayList<String> modlistS = entry.getValue();
if(modlistS.isEmpty()) {
for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
if (abstractMod.getName().equals(modS)) {
modList.add(abstractMod);
break; break;
} }
} }
} else {
for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
if (abstractMod.getName().equals(modS)) {
if(!(abstractMod instanceof Mod)) continue;
Mod m = ((Mod) abstractMod).clone();
for (int i = 0; i < m.getFiles().size(); i++) {
boolean found = false;
for (String pathS : modlistS) {
if(m.getFiles().get(i).getModfileString().equals(pathS)) {
found = true;
}
} }
if(!found) { if (fileChecker.getAdded().containsKey(modname)) {
m.getFiles().remove(i); for (ModFile modFile : fileChecker.getAdded().get(modname)) {
if (String.join("/", modFile.getPath()).equals(treePath)) {
synclist.add(modFile);
break;
} }
} }
}
}
synclist.setDeleted(fileChecker.getDeleted());
modList.add(m); return synclist;
}
}
}
}
return modList;
} }
public void updateModList(Modset modset) { public void updateModList(Modset modset) {
ListModel<String> model = (ListModel) modList.getModel(); ListModel<String> model = (ListModel) modList.getModel();
// TODO: Show All Mods (keyname) // TODO: Show All Mods (keyname)
// TODO: Show not installed Mods with red font // Show not installed Mods with red font
// TODO: Select Mod if in modset.Mods // Select Mod if in modset.Mods
// TODO: Custom Checkbox Render // Custom Checkbox Render
// TODO: Wenn modset.type == Server alle Checkboxen deaktivieren! // Wenn modset.type == Server alle Checkboxen deaktivieren!
} }
public void updateRepoTree() { public void updateRepoTree() {
@ -629,28 +636,26 @@ public class LauncherGUI implements Observer {
collapseAllButton.setEnabled(false); collapseAllButton.setEnabled(false);
DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel(); DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot(); RepositoryTreeNode root = (RepositoryTreeNode) model.getRoot();
root.setUserObject("Repository");
root.removeAllChildren(); root.removeAllChildren();
for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) { for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
if (abstractMod instanceof Mod) { if (abstractMod instanceof Mod) {
// Whole Folder // Whole Folder
// TODO: Recursives Ordner Parsen und einzelne Treenodes erstellen
Mod m = (Mod) abstractMod; Mod m = (Mod) abstractMod;
DefaultMutableTreeNode modFolder = new DefaultMutableTreeNode(m.getName(), true); RepositoryTreeNode modFolder = new RepositoryTreeNode(m.getName(), true);
model.insertNodeInto(modFolder, root, root.getChildCount()); model.insertNodeInto(modFolder, root, root.getChildCount());
for (ModFile modfile : m.getFiles()) { for (ModFile modfile : m.getFiles()) {
DefaultMutableTreeNode lastNode = modFolder; RepositoryTreeNode lastNode = modFolder;
ArrayList<String> path = modfile.getPath(); ArrayList<String> path = modfile.getPath();
for (int i = 0; i < path.size(); i++) { for (int i = 0; i < path.size() -1; i++) {
boolean found = false; boolean found = false;
for (int j = 0; j < lastNode.getChildCount(); j++) { for (int j = 0; j < lastNode.getChildCount(); j++) {
DefaultMutableTreeNode temp = (DefaultMutableTreeNode) lastNode.getChildAt(j); RepositoryTreeNode temp = (RepositoryTreeNode) lastNode.getChildAt(j);
if (temp.getUserObject().equals(path.get(i))) { if (temp.getUserObject().equals(path.get(i))) {
found = true; found = true;
lastNode = temp; lastNode = temp;
@ -659,26 +664,25 @@ public class LauncherGUI implements Observer {
} }
if (!found) { if (!found) {
DefaultMutableTreeNode temp = new DefaultMutableTreeNode(path.get(i)); RepositoryTreeNode temp = new RepositoryTreeNode(path.get(i));
model.insertNodeInto(temp, lastNode, lastNode.getChildCount()); model.insertNodeInto(temp, lastNode, lastNode.getChildCount());
lastNode = temp; lastNode = temp;
} }
} }
model.insertNodeInto(new RepositoryTreeNode(modfile.getName(), getNodeColor(m.getName(), modfile)), lastNode, lastNode.getChildCount());
model.insertNodeInto(new DefaultMutableTreeNode(modfile.getName()), lastNode, lastNode.getChildCount());
} }
sort(modFolder); sort(modFolder);
} else if (abstractMod instanceof ModFile) { } else if (abstractMod instanceof ModFile) {
// Just a Single FIle // Just a Single FIle
ModFile m = (ModFile) abstractMod; ModFile m = (ModFile) abstractMod;
model.insertNodeInto(new DefaultMutableTreeNode(m.getName(), false), root, root.getChildCount()); model.insertNodeInto(new RepositoryTreeNode(m.getName(), getNodeColor(m.getName(), m), false), root, root.getChildCount());
} }
} }
sort(root); sort(root);
setParentColor(root);
repoTree.clearCheckChangeEventListeners(); repoTree.clearCheckChangeEventListeners();
repoTree.resetCheckingState(); repoTree.resetCheckingState();
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
@ -690,19 +694,71 @@ public class LauncherGUI implements Observer {
updateTreePanel.repaint(); updateTreePanel.repaint();
}); });
repoTree.addCheckChangeEventListener(new JCheckBoxTree.CheckChangeEventListener() {
@Override
public void checkStateChanged(JCheckBoxTree.CheckChangeEvent event) {
lastSynclist = getSyncList();
if (lastSynclist.getSize() != 0)
syncSizeLabel.setText(Humanize.binaryPrefix(lastSynclist.getSize()));
else syncSizeLabel.setText("0.0 B");
if (lastSynclist.getCount() != 0) {
syncDownloadButton.setEnabled(true);
syncFileCountLabel.setText("" + lastSynclist.getCount());
} else {
syncDownloadButton.setEnabled(false);
syncFileCountLabel.setText("");
}
}
});
expandAllButton.setEnabled(true); expandAllButton.setEnabled(true);
collapseAllButton.setEnabled(true); collapseAllButton.setEnabled(true);
} }
public DefaultMutableTreeNode sort(DefaultMutableTreeNode node) { public Color getNodeColor(String mod, ModFile mf) {
if (fileChecker.getAdded().containsKey(mod)) {
ArrayList<ModFile> mfList = fileChecker.getAdded().get(mod);
for (ModFile modFile : mfList) {
if (modFile.getLocaleFile().getPath().equals(mf.getLocaleFile().getPath())) return Color.RED;
}
}
if (fileChecker.getChanged().containsKey(mod)) {
ArrayList<ModFile> mfList = fileChecker.getChanged().get(mod);
for (ModFile modFile : mfList) {
if (modFile.getLocaleFile().getPath().equals(mf.getLocaleFile().getPath())) return Color.ORANGE;
}
}
return null;
}
public void setParentColor(RepositoryTreeNode node) {
for (TreeNode leaf : node.getAllLeafNodes()) {
if (!(leaf instanceof RepositoryTreeNode)) continue;
RepositoryTreeNode mLeaf = (RepositoryTreeNode) leaf;
TreeNode[] path = mLeaf.getPath();
if (mLeaf.getLabelColor() == null) continue;
for (int i = 0; i < path.length - 1; i++) {
if (!(path[i] instanceof RepositoryTreeNode)) continue;
RepositoryTreeNode parent = (RepositoryTreeNode) path[i];
if (parent.getLabelColor() == mLeaf.getLabelColor()) continue;
if (parent.getLabelColor() == Color.RED) continue;
parent.setLabelColor(mLeaf.getLabelColor());
}
}
}
public RepositoryTreeNode sort(RepositoryTreeNode node) {
//sort alphabetically //sort alphabetically
for (int i = 0; i < node.getChildCount() - 1; i++) { for (int i = 0; i < node.getChildCount() - 1; i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); RepositoryTreeNode child = (RepositoryTreeNode) node.getChildAt(i);
String nt = child.getUserObject().toString(); String nt = child.getUserObject().toString();
for (int j = i + 1; j <= node.getChildCount() - 1; j++) { for (int j = i + 1; j <= node.getChildCount() - 1; j++) {
DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); RepositoryTreeNode prevNode = (RepositoryTreeNode) node.getChildAt(j);
String np = prevNode.getUserObject().toString(); String np = prevNode.getUserObject().toString();
if (nt.compareToIgnoreCase(np) > 0) { if (nt.compareToIgnoreCase(np) > 0) {
@ -717,9 +773,9 @@ public class LauncherGUI implements Observer {
//put folders first - normal on Windows and some flavors of Linux but not on Mac OS X. //put folders first - normal on Windows and some flavors of Linux but not on Mac OS X.
for (int i = 0; i < node.getChildCount() - 1; i++) { for (int i = 0; i < node.getChildCount() - 1; i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); RepositoryTreeNode child = (RepositoryTreeNode) node.getChildAt(i);
for (int j = i + 1; j <= node.getChildCount() - 1; j++) { for (int j = i + 1; j <= node.getChildCount() - 1; j++) {
DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); RepositoryTreeNode prevNode = (RepositoryTreeNode) node.getChildAt(j);
if (!prevNode.isLeaf() && child.isLeaf()) { if (!prevNode.isLeaf() && child.isLeaf()) {
node.insert(child, j); node.insert(child, j);
@ -783,25 +839,50 @@ public class LauncherGUI implements Observer {
syncCheckAbortButton.setEnabled(false); syncCheckAbortButton.setEnabled(false);
syncCheckStatusLabel.setText("Finished!"); syncCheckStatusLabel.setText("Finished!");
updateRepoTree(); updateRepoTree();
// TODO: Label einfärben
// TODO: Enable Tree Checkboxes repoTree.setCheckboxesEnabled(true);
syncDownloadButton.setEnabled(true); syncDownloadButton.setEnabled(true);
syncAddedFilesLabel.setText(String.valueOf(fileChecker.getAddedCount())); syncAddedFilesLabel.setText(String.valueOf(fileChecker.getAddedCount()));
syncChangedFilesLabel.setText(String.valueOf(fileChecker.getChangedCount())); syncChangedFilesLabel.setText(String.valueOf(fileChecker.getChangedCount()));
syncDeletedFilesLabel.setText(String.valueOf(fileChecker.getDeletedCount())); syncDeletedFilesLabel.setText(String.valueOf(fileChecker.getDeletedCount()));
syncSizeLabel.setText(String.valueOf(fileChecker.getSize())); // TODO: Make Humanreadable syncDownloadAbortButton.setEnabled(false);
syncDownloadButton.setEnabled(true);
syncPauseButton.setEnabled(false);
syncChangedFileSizeLabel.setText(Humanize.binaryPrefix(fileChecker.getSize()));
} else if (s.equals("fileCheckerStopped")) { } else if (s.equals("fileCheckerStopped")) {
syncCheckButton.setEnabled(true); syncCheckButton.setEnabled(true);
syncCheckAbortButton.setEnabled(false); syncCheckAbortButton.setEnabled(false);
syncCheckProgress.setValue(0); syncCheckProgress.setValue(0);
syncCheckStatusLabel.setText("Failed!"); syncCheckStatusLabel.setText("Failed!");
repoTree.setCheckboxesEnabled(false);
syncDownloadAbortButton.setEnabled(false);
syncDownloadButton.setEnabled(false);
syncPauseButton.setEnabled(false);
repoTree.setCheckboxesChecked(false);
syncAddedFilesLabel.setText("" + 0); syncAddedFilesLabel.setText("" + 0);
syncChangedFilesLabel.setText("" + 0); syncChangedFilesLabel.setText("" + 0);
syncDeletedFilesLabel.setText("" + 0); syncDeletedFilesLabel.setText("" + 0);
syncSizeLabel.setText("0.0 B"); syncChangedFileSizeLabel.setText("0.0 B");
} else if (s.equals("syncStopped")) {
new Thread(() -> fileChecker.check()).start();
} else if (s.equals("syncComplete")) {
new Thread(() -> fileChecker.check()).start();
} else if (s.equals("syncContinue")) {
syncDownloadAbortButton.setEnabled(true);
syncPauseButton.setEnabled(true);
syncPauseButton.setText(LangUtils.getInstance().getString("pause"));
syncDownloadButton.setEnabled(false);
} else if (s.equals("syncPaused")) {
syncDownloadAbortButton.setEnabled(true);
syncPauseButton.setEnabled(true);
syncPauseButton.setText(LangUtils.getInstance().getString("resume"));
syncDownloadButton.setEnabled(false);
} }
} }
} }

View File

@ -17,6 +17,7 @@ import java.util.EventListener;
import java.util.EventObject; import java.util.EventObject;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
/** /**
* Created by SomethingSomething https://stackoverflow.com/a/21851201/5605489 * Created by SomethingSomething https://stackoverflow.com/a/21851201/5605489
@ -31,6 +32,7 @@ public class JCheckBoxTree extends JTree {
// Defining data structure that will enable to fast check-indicate the state of each node // Defining data structure that will enable to fast check-indicate the state of each node
// It totally replaces the "selection" mechanism of the JTree // It totally replaces the "selection" mechanism of the JTree
private class CheckedNode { private class CheckedNode {
boolean isEnabled;
boolean isSelected; boolean isSelected;
boolean hasChildren; boolean hasChildren;
boolean allChildrenSelected; boolean allChildrenSelected;
@ -38,6 +40,7 @@ public class JCheckBoxTree extends JTree {
public CheckedNode(boolean isSelected_, boolean hasChildren_, boolean allChildrenSelected_) { public CheckedNode(boolean isSelected_, boolean hasChildren_, boolean allChildrenSelected_) {
isSelected = isSelected_; isSelected = isSelected_;
hasChildren = hasChildren_; hasChildren = hasChildren_;
isEnabled = true;
allChildrenSelected = allChildrenSelected_; allChildrenSelected = allChildrenSelected_;
} }
} }
@ -107,6 +110,11 @@ public class JCheckBoxTree extends JTree {
return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected; return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected;
} }
public boolean isSelected(TreePath path) {
CheckedNode cn = nodesCheckingState.get(path);
return cn.isSelected;
}
public void resetCheckingState() { public void resetCheckingState() {
nodesCheckingState = new HashMap<TreePath, CheckedNode>(); nodesCheckingState = new HashMap<TreePath, CheckedNode>();
checkedPaths = new HashSet<TreePath>(); checkedPaths = new HashSet<TreePath>();
@ -148,12 +156,21 @@ public class JCheckBoxTree extends JTree {
boolean hasFocus) { boolean hasFocus) {
checkBox.setText(value.toString()); checkBox.setText(value.toString());
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node instanceof RepositoryTreeNode && ((RepositoryTreeNode) node).getLabelColor() != null) {
checkBox.setForeground(((RepositoryTreeNode) node).getLabelColor());
} else {
checkBox.setForeground(UIManager.getColor("CheckBox.foreground"));
}
TreePath tp = new TreePath(node.getPath()); TreePath tp = new TreePath(node.getPath());
CheckedNode cn = nodesCheckingState.get(tp); CheckedNode cn = nodesCheckingState.get(tp);
if (cn == null) { if (cn == null) {
return this; return this;
} }
checkBox.setSelected(cn.isSelected); checkBox.setSelected(cn.isSelected);
checkBox.setEnabled(cn.isEnabled);
checkBox.setOpaque(cn.isSelected && cn.hasChildren && !cn.allChildrenSelected); checkBox.setOpaque(cn.isSelected && cn.hasChildren && !cn.allChildrenSelected);
return this; return this;
} }
@ -264,6 +281,26 @@ public class JCheckBoxTree extends JTree {
} }
} }
public void setCheckboxesChecked(boolean state) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
checkSubTree(new TreePath(node.getPath()), state);
}
public void setCheckboxesEnabled(TreePath tp, boolean state) {
CheckedNode cn = nodesCheckingState.get(tp);
cn.isEnabled = state;
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp.getLastPathComponent();
for (int i = 0; i < node.getChildCount(); i++) {
setCheckboxesEnabled(tp.pathByAddingChild(node.getChildAt(i)), state);
}
}
public void setCheckboxesEnabled(boolean state) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
setCheckboxesEnabled(new TreePath(node.getPath()), state);
}
public void expandAllNodes() { public void expandAllNodes() {
setTreeExpandedState(true); setTreeExpandedState(true);
} }

View File

@ -0,0 +1,54 @@
package de.mc8051.arma3launcher.model;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.util.HashSet;
import java.util.Set;
/**
* Created by gurkengewuerz.de on 26.03.2020.
*/
public class RepositoryTreeNode extends DefaultMutableTreeNode {
private Color labelColor = null;
public RepositoryTreeNode(String userObject, boolean allowChildren) {
super(userObject, allowChildren);
}
public RepositoryTreeNode(String userObject, Color labelColor, boolean allowChildren) {
super(userObject, allowChildren);
this.labelColor = labelColor;
}
public RepositoryTreeNode(String userObject, Color labelColor) {
super(userObject);
this.labelColor = labelColor;
}
public RepositoryTreeNode(String userObject) {
super(userObject);
}
public Color getLabelColor() {
return labelColor;
}
public void setLabelColor(Color labelColor) {
this.labelColor = labelColor;
}
public Set<TreeNode> getAllLeafNodes() {
Set<TreeNode> leafNodes = new HashSet<>();
if (this.children == null) {
leafNodes.add(this);
} else {
for (Object child : this.children) {
if (child instanceof RepositoryTreeNode)
leafNodes.addAll(((RepositoryTreeNode) child).getAllLeafNodes());
}
}
return leafNodes;
}
}

View File

@ -1,10 +1,19 @@
package de.mc8051.arma3launcher.objects; package de.mc8051.arma3launcher.objects;
import de.mc8051.arma3launcher.ArmA3Launcher;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* Created by gurkengewuerz.de on 25.03.2020. * Created by gurkengewuerz.de on 25.03.2020.
@ -17,8 +26,11 @@ public class ModFile implements AbstractMod {
private String filename; private String filename;
private String extension; private String extension;
private String modfileString; private String modfileString;
private String sha1sum;
private String parent;
private String localGeneratedSHA1sum = "";
public ModFile(File f, String modfile, long size) { public ModFile(File f, String modfile, String parent, long size, String sha1sum) {
// File: Abosolut Path // File: Abosolut Path
// modfile: addons/config/something.pbo // modfile: addons/config/something.pbo
// size: size as in metafile on server // size: size as in metafile on server
@ -28,6 +40,12 @@ public class ModFile implements AbstractMod {
this.filename = FilenameUtils.getBaseName(modfile); this.filename = FilenameUtils.getBaseName(modfile);
this.extension = FilenameUtils.getExtension(modfile); this.extension = FilenameUtils.getExtension(modfile);
this.modfileString = modfile; this.modfileString = modfile;
this.sha1sum = sha1sum.toLowerCase();
this.parent = parent;
}
public ModFile(File f, String modfile, long size, String sha1sum) {
this(f, modfile, null, size, sha1sum);
} }
public long getSize() { public long getSize() {
@ -49,11 +67,11 @@ public class ModFile implements AbstractMod {
public ArrayList<String> getPath() { public ArrayList<String> getPath() {
ArrayList<String> list = new ArrayList<>(); ArrayList<String> list = new ArrayList<>();
File relativePath = new File("./" + modfileString); File relativePath = new File("./" + modfileString);
do { do {
list.add(relativePath.getName()); list.add(relativePath.getName());
relativePath = relativePath.getParentFile(); relativePath = relativePath.getParentFile();
} while (relativePath.getParentFile() != null); } while (relativePath.getParentFile() != null);
list.remove(0);
Collections.reverse(list); Collections.reverse(list);
return list; return list;
} }
@ -79,4 +97,45 @@ public class ModFile implements AbstractMod {
public File getLocaleFile() { public File getLocaleFile() {
return f; return f;
} }
public String getSHA1Sum() {
return sha1sum;
}
public String getLocalGeneratedSHA1Sum() {
try {
if (localGeneratedSHA1sum.isEmpty() && exists()) {
localGeneratedSHA1sum = DigestUtils.sha1Hex(new FileInputStream(f.getAbsolutePath())).toLowerCase();
}
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
return localGeneratedSHA1sum;
}
public String getRemoteFile() {
String s = ArmA3Launcher.config.getString("sync.url");
if (parent == null || parent.isEmpty()) {
return s + "/" + encodeToURL(getName());
}
s += "/" + encodeToURL(parent);
for (String seg : getPath()) {
s += "/" + encodeToURL(seg);
}
return s;
}
public String getParent() {
return parent;
}
private String encodeToURL(String s) {
try {
return URLEncoder.encode(s, StandardCharsets.UTF_8.name()).replace("+", "%20").replace("@", "%40");
} catch (UnsupportedEncodingException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
return "";
}
} }

View File

@ -1,70 +0,0 @@
package de.mc8051.arma3launcher.repo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by gurkengewuerz.de on 24.03.2020.
*/
public class DownloadThread implements Runnable {
private ProcessBuilder processBuilder;
private Process process;
private Thread thread;
private Status status = Status.PENDING;
public DownloadThread(ProcessBuilder processBuilder) {
this.processBuilder = processBuilder;
this.processBuilder.redirectErrorStream(true);
thread = new Thread(this);
thread.start();
}
public void stop() {
process.destroy();
thread.interrupt();
}
public Status getStatus() {
return status;
}
@Override
public void run() {
try {
process = processBuilder.start();
status = Status.RUNNING;
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ( (line = reader.readLine()) != null && !thread.isInterrupted()) {
System.out.println(line);
}
int exitVal = process.waitFor();
if(exitVal == 0) status = Status.FINNISHED;
else status = Status.ERROR;
System.out.println(exitVal);
} catch (IOException | InterruptedException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
status = Status.ERROR;
}
}
private enum Status {
PENDING(0),
RUNNING(1),
FINNISHED(2),
ERROR(3);
Status(int i) {
}
}
}

View File

@ -30,10 +30,10 @@ public class FileChecker implements Observable {
private JProgressBar pb; private JProgressBar pb;
private boolean stop = false; private boolean stop = false;
ArrayList<Path> deleted = new ArrayList<>(); private ArrayList<Path> deleted = new ArrayList<>();
HashMap<String, ArrayList<ModFile>> changed = new HashMap<>(); private HashMap<String, ArrayList<ModFile>> changed = new HashMap<>();
int changedCount = 0; int changedCount = 0;
HashMap<String, ArrayList<ModFile>> added = new HashMap<>(); private HashMap<String, ArrayList<ModFile>> added = new HashMap<>();
int addedCount = 0; int addedCount = 0;
long size = 0; long size = 0;
@ -66,7 +66,8 @@ public class FileChecker implements Observable {
Mod m = (Mod) abstractMod; Mod m = (Mod) abstractMod;
for (ModFile mf : m.getFiles()) { for (ModFile mf : m.getFiles()) {
checkFile(mf.getName(), mf); checkFile(m.getName(), mf);
i++; i++;
int finalI = i; int finalI = i;
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
@ -99,16 +100,21 @@ public class FileChecker implements Observable {
} }
private void checkFile(String mod, ModFile mf) { private void checkFile(String mod, ModFile mf) {
// TODO: Add mf to Array if Array already exists ArrayList<ModFile> temp = new ArrayList<>();
if(!mf.exists()) { if(!mf.exists()) {
added.put(mod, mf); if(added.containsKey(mod)) temp =added.get(mod);
temp.add(mf);
added.put(mod, temp);
addedCount++; addedCount++;
size += mf.getSize(); size += mf.getSize();
return; return;
} }
if(mf.getLocalSize() != mf.getSize()) { if(mf.getLocalSize() != mf.getSize() || !mf.getSHA1Sum().equalsIgnoreCase(mf.getLocalGeneratedSHA1Sum())) {
changed.put(mod, mf); if(changed.containsKey(mod)) temp =changed.get(mod);
temp.add(mf);
changed.put(mod, temp);
changedCount++; changedCount++;
size += mf.getSize(); size += mf.getSize();
return; return;

View File

@ -151,16 +151,18 @@ public class RepositoryManger implements Observable {
Iterator<String> keys = content.keys(); Iterator<String> keys = content.keys();
while (keys.hasNext()) { while (keys.hasNext()) {
String modfile = keys.next(); String modfile = keys.next();
long modfilesize = content.getLong(modfile); JSONObject jo = content.getJSONObject(modfile);
long modfilesize = jo.getLong("size");
String sha1 = jo.getString("sha1");
modFiles.add(new ModFile(new File(finalModPath + File.separator + modname + File.separator + modfile), modfile, modfilesize)); modFiles.add(new ModFile(new File(finalModPath + File.separator + modname + File.separator + modfile), modfile, modname, modfilesize, sha1));
RepositoryManger.MOD_LIST_SIZE++; RepositoryManger.MOD_LIST_SIZE++;
} }
MOD_LIST.add(new Mod(modname, modsize, modFiles)); MOD_LIST.add(new Mod(modname, modsize, modFiles));
} else { } else {
// Single File // Single File
MOD_LIST.add(new ModFile(new File(finalModPath + File.separator + modname), modname, modsize)); MOD_LIST.add(new ModFile(new File(finalModPath + File.separator + modname), modname, modsize, jsonMod.getString("sha1")));
RepositoryManger.MOD_LIST_SIZE++; RepositoryManger.MOD_LIST_SIZE++;
} }

View File

@ -0,0 +1,108 @@
package de.mc8051.arma3launcher.repo;
import de.mc8051.arma3launcher.objects.AbstractMod;
import de.mc8051.arma3launcher.objects.Mod;
import de.mc8051.arma3launcher.objects.ModFile;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* Created by gurkengewuerz.de on 26.03.2020.
*/
public class SyncList extends ArrayList<AbstractMod> {
private long size = 0;
private int count = 0;
private ArrayList<Path> deleted = new ArrayList<>();
private SyncList(long size, int count) {
this.size = size;
this.count = count;
}
public SyncList() {
}
public void setDeleted(ArrayList<Path> deleted) {
this.deleted = deleted;
}
@Override
public boolean add(AbstractMod abstractMod) {
addedMod(abstractMod);
return super.add(abstractMod);
}
@Override
public boolean addAll(Collection<? extends AbstractMod> c) {
for (AbstractMod abstractMod : c) {
addedMod(abstractMod);
}
return super.addAll(c);
}
private void addedMod(AbstractMod abstractMod) {
if (abstractMod instanceof Mod) {
Mod mod = (Mod) abstractMod;
for (ModFile mf : mod.getFiles()) {
size += mf.getSize();
count++;
}
} else if (abstractMod instanceof ModFile) {
ModFile mf = (ModFile) abstractMod;
size += mf.getSize();
count++;
}
}
@Override
public AbstractMod remove(int index) {
AbstractMod abstractMod = get(index);
if (abstractMod instanceof Mod) {
Mod mod = (Mod) abstractMod;
for (ModFile mf : mod.getFiles()) {
size -= mf.getSize();
}
} else if (abstractMod instanceof ModFile) {
ModFile mf = (ModFile) abstractMod;
size -= mf.getSize();
}
return super.remove(index);
}
public void setSize(long size) {
this.size = size;
}
public void setCount(int count) {
this.count = count;
}
public SyncList clone() {
SyncList clone = new SyncList();
clone.addAll(this);
clone.getDeleted().addAll(deleted);
clone.setSize(size);
clone.setDeleted(deleted);
return clone;
}
public ArrayList<Path> getDeleted() {
return deleted;
}
public long getSize() {
return size;
}
public int getCount() {
return count;
}
}

View File

@ -1,8 +1,304 @@
package de.mc8051.arma3launcher.repo; package de.mc8051.arma3launcher.repo;
import co.bitshfted.xapps.zsync.Zsync;
import co.bitshfted.xapps.zsync.ZsyncException;
import co.bitshfted.xapps.zsync.ZsyncObserver;
import de.mc8051.arma3launcher.ArmA3Launcher;
import de.mc8051.arma3launcher.LauncherGUI;
import de.mc8051.arma3launcher.interfaces.Observable;
import de.mc8051.arma3launcher.interfaces.Observer;
import de.mc8051.arma3launcher.objects.AbstractMod;
import de.mc8051.arma3launcher.objects.ModFile;
import humanize.Humanize;
import javax.swing.*;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* Created by gurkengewuerz.de on 25.03.2020. * Created by gurkengewuerz.de on 25.03.2020.
*/ */
public class Syncer { public class Syncer extends ZsyncObserver implements Observable {
// FilenameUtils.directoryContains
private List<Observer> observerList = new ArrayList<>();
private boolean stopped = false;
private boolean paused = false;
private boolean running = false;
private ModFile currentDownload = null;
private SyncList modlist;
private boolean currentDownload_failed = false;
private boolean controlfile_downloaded = false;
private int failed = 0;
private int success = 0;
long syncSize;
int syncCount;
long downloadStarted;
long downloadEnded;
long downloadSize;
long downloadDownloaded;
private Zsync zsync;
private LauncherGUI gui;
public Syncer(LauncherGUI gui) {
zsync = new Zsync();
this.gui = gui;
}
public void sync(SyncList ml) {
modlist = ml;
stopped = false;
paused = false;
running = true;
currentDownload = null;
failed = 0;
success = 0;
syncSize = ml.getSize();
syncCount = ml.getCount();
SwingUtilities.invokeLater(() -> {
gui.syncDownloadProgress.setMaximum(syncCount);
gui.syncDownloadProgress.setValue(0);
});
boolean lastPause = false;
while (running) {
if (stopped) {
running = false;
break;
}
if (modlist.isEmpty()) {
running = false;
break;
}
if (paused) {
if (!lastPause) {
lastPause = true;
notifyObservers("syncPaused");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
continue;
} else if (lastPause) {
lastPause = false;
notifyObservers("syncContinue");
}
if (currentDownload != null) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
}
AbstractMod abstractMod = modlist.get(0);
ModFile mf = null;
if (abstractMod instanceof ModFile) {
mf = (ModFile) abstractMod;
}
if (mf != null) {
Zsync.Options o = new Zsync.Options();
o.setOutputFile(Paths.get(mf.getLocaleFile().getAbsolutePath()));
try {
currentDownload = mf;
currentDownload_failed = false;
controlfile_downloaded = false;
zsync.zsync(URI.create(mf.getRemoteFile() + ".zsync"), o, this);
} catch (ZsyncException | IllegalArgumentException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
} else {
modlist.remove(0);
}
}
deleteFiles();
cleanUpEmptyFolders();
if (stopped) {
notifyObservers("syncStopped");
} else {
notifyObservers("syncComplete");
}
}
public void finnishCurrent() {
modlist.remove(0);
currentDownload = null;
}
public void deleteFiles() {
modlist.getDeleted().stream()
.filter((p) -> p.toFile().exists())
.filter((p) -> p.toFile().canRead())
.filter((p) -> p.toFile().canWrite())
.forEach((p) -> p.toFile().delete());
}
public void cleanUpEmptyFolders() {
try {
String modPath = ArmA3Launcher.user_config.get("client", "modPath");
if (modPath == null) modPath = "";
if (modPath.isEmpty()) return;
Files.find(Paths.get(modPath),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isDirectory())
.filter((p) -> p.toFile().canRead())
.filter((p) -> p.toFile().canWrite())
.filter((p) -> p.toFile().list().length == 0)
.forEach((p) -> p.toFile().delete());
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
}
@Override
public void zsyncStarted(URI requestedZsyncUri, Zsync.Options options) {
super.zsyncStarted(requestedZsyncUri, options);
System.out.println("ZSync started " + options.getOutputFile());
}
@Override
public void controlFileDownloadingComplete() {
super.controlFileDownloadingComplete();
System.out.println("controlFileDownloadingComplete");
controlfile_downloaded = true;
}
@Override
public void zsyncFailed(Exception exception) {
super.zsyncFailed(exception);
currentDownload_failed = true;
System.out.println("Zsync failed " + exception.getMessage());
}
@Override
public void zsyncComplete() {
super.zsyncComplete();
downloadEnded = System.nanoTime();
System.out.println(downloadSize);
System.out.println(downloadEnded - downloadStarted);
System.out.println((downloadSize / (downloadEnded - downloadStarted)) / 1000);
System.out.println("Zsync complete");
if (currentDownload_failed)
failed++;
else success++;
final long finalSize = syncSize - modlist.getSize();
int i = success + failed;
int percentage = (int) ((double)i / (double)Long.valueOf(syncCount).intValue() * 100);
SwingUtilities.invokeLater(() -> {
gui.syncDownloadProgress.setValue(i);
gui.syncDownloadedLabel.setText(Humanize.binaryPrefix(finalSize) + " " + " (" + failed + " failed)");
gui.syncDownloadProgress.setString(percentage + "%");
});
finnishCurrent();
}
@Override
public void controlFileDownloadingStarted(URI uri, long length) {
super.controlFileDownloadingStarted(uri, length);
System.out.println("controlFileDownloadingStarted " + length);
}
@Override
public void remoteFileDownloadingStarted(URI uri, long length) {
super.remoteFileDownloadingStarted(uri, length);
System.out.println("remoteFileDownloadingStarted " + length);
SwingUtilities.invokeLater(() -> {
gui.syncFileProgress.setMaximum(Long.valueOf(length).intValue());
gui.syncFileProgress.setValue(0);
});
downloadSize = length;
downloadDownloaded = 0;
downloadStarted = System.nanoTime();
}
@Override
public void bytesDownloaded(long bytes) {
super.bytesDownloaded(bytes);
// System.out.println("Downloaded " + bytes);
downloadDownloaded += bytes;
// TODO: Fix file Download Progress
if (controlfile_downloaded) {
SwingUtilities.invokeLater(() -> {
gui.syncFileProgress.setValue(Long.valueOf(downloadDownloaded).intValue());
});
}
}
public boolean isStopped() {
return stopped;
}
public boolean isPaused() {
return paused;
}
public boolean isRunning() {
return running;
}
public void stop() {
this.stopped = true;
}
public void setPaused(boolean paused) {
this.paused = paused;
}
public int getCountFailed() {
return failed;
}
public int getCountSuccess() {
return success;
}
@Override
public void addObserver(Observer observer) {
observerList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observerList.remove(observer);
}
@Override
public void notifyObservers(String obj) {
for (Observer obs : observerList) obs.update(obj);
}
} }

View File

@ -88,6 +88,12 @@ collapse_all=Alles einklappen
same_mod_arma_dir_msg=Das ArmA sowie Mod Verzeichnis dürfen nicht identisch sein. same_mod_arma_dir_msg=Das ArmA sowie Mod Verzeichnis dürfen nicht identisch sein.
same_mod_arma_dir=Gleiches Verzeichnis same_mod_arma_dir=Gleiches Verzeichnis
check_local_addons=Lokale Dateien überprüfen check_local_addons=Lokale Dateien überprüfen
changed_files=Veränderte Datien changed_filesize=Veränderte Größe
added_files=Hinzugefügte Datien added_files=Hinzugefügte Dateien
deleted_files=Gelöschte Dateien deleted_files=Gelöschte Dateien
changed_filesize=Veränderte Größe
changed_filesize_tooltip=Dies ist die ungefähre Dateigröße die Maximal runtergeladen wird
resume=Fortsetzen
changed_filesize=Veränderte Dateien
changed_files=Geänderte Dateien
file_count=Dateianzahl

View File

@ -86,3 +86,12 @@ repository_content=Repository Content
collapse_all=Collapse All collapse_all=Collapse All
same_mod_arma_dir_msg=The ArmA and Mod directory must not be identical. same_mod_arma_dir_msg=The ArmA and Mod directory must not be identical.
same_mod_arma_dir=Same directory same_mod_arma_dir=Same directory
resume=Resume
added_files=Added files
deleted_files=Deleted files
update_repository=Update repository
check_local_addons=Check local files
changed_filesize=Changed size
changed_files=Changed files
changed_filesize_tooltip=This is the approximate maximum file size that will be downloaded
file_count=Number of files