wörk wörk

This commit is contained in:
Niklas 2020-03-26 02:26:31 +01:00
parent 9e1509872a
commit 11bd1c8269
19 changed files with 1331 additions and 109 deletions

View File

@ -28,15 +28,15 @@ while IFS= read -r line; do
mustgenerate=false
zsyncfile="${line}.zsync"
filebyte=$(wc -c < ${line})
filedate=$(stat -c %Y ${line})
filebyte=$(wc -c < "${line}")
filedate=$(stat -c %Y "${line}")
zsyncfiledate=$(strings ${zsyncfile} 2>/dev/null | grep -m 1 MTime | cut -d" " -f2-)
zsyncfiledate=$(strings "${zsyncfile}" 2>/dev/null | grep -m 1 MTime | cut -d" " -f2-)
if [ ! -f "$zsyncfile" ]; then
echo "$zsyncfile does not exist"
mustgenerate=true
elif [[ ! $(strings ${zsyncfile} | grep -m 1 Length | cut -d" " -f2) == $filebyte ]]; then # Check file length
elif [[ ! $(strings "${zsyncfile}" | grep -m 1 Length | cut -d" " -f2) == $filebyte ]]; then # Check file length
echo "$zsyncfile does not have corret length"
mustgenerate=true
elif [[ ! $filedate == $(date -d "${zsyncfiledate}" +"%s") ]]; then # Check date
@ -46,11 +46,11 @@ while IFS= read -r line; do
if [ "$mustgenerate" = true ]; then
echo "Generate $zsyncfile"
rm ${zsyncfile} 2> /dev/null
dirfile=$(dirname ${line})
filename=$(basename ${line})
filenamezsync=$(basename ${zsyncfile})
$(cd ${dirfile} && zsyncmake -o ${filenamezsync} ${filename})
rm "${zsyncfile}" 2> /dev/null
dirfile=$(dirname "${line}")
filename=$(basename "${line}")
filenamezsync=$(basename "${zsyncfile}")
$(cd "${dirfile}" && zsyncmake -o "${filenamezsync}" "${filename}")
if [ $? -eq 0 ]; then
echo "Success: Generated ${zsyncfile}"
else
@ -67,10 +67,10 @@ echo -e "===== ===== ===== ===== ===== =====\n"
echo "===== ===== ===== DELETE SINGLE ZFILE WITHOUT FILE ===== ===== ====="
ZSYNCLIST=$(find . -name "*.zsync")
while IFS= read -r zfile; do
ORIG=$(echo ${zfile} | rev | cut -c7- | rev)
ORIG=$(echo "${zfile}" | rev | cut -c7- | rev)
if [ ! -f "$ORIG" ]; then
echo "$ORIG does not exist"
rm ${zfile}
rm "${zfile}"
fi
done <<< "$ZSYNCLIST"
echo -e "===== ===== ===== ===== ===== =====\n"
@ -85,18 +85,18 @@ while IFS= read -r folder; do
echo "is dir"
x=""
foldersize=0
FILEFOLDER=$(find ${folder} -type f ! -path "*.zsync" | sed 's|^./||')
FILEFOLDER=$(find "${folder}" -type f ! -path "*.zsync" | sed 's|^./||')
while IFS= read -r folderfile; do
filebyte=$(wc -c < ${folderfile})
filebyte=$(wc -c < "${folderfile}")
foldersize=$(expr $foldersize + $filebyte)
name=$(echo ${folderfile} | cut -d"/" -f2-)
name=$(echo "${folderfile}" | cut -d"/" -f2-)
x="\"${name}\":${filebyte},${x}"
done <<< "$FILEFOLDER"
x=$(echo ${x} | rev | cut -c2- | rev)
JSONDATA+=( "\"${folder}\": {\"size\":${foldersize},\"content\":{${x}}}" )
else
echo "is file"
filebyte=$(wc -c < ${folder})
filebyte=$(wc -c < "${folder}")
JSONDATA+=( "\"${folder}\": {\"size\":${filebyte}}" )
fi
done <<< "$FILELIST"

View File

@ -57,6 +57,11 @@
<artifactId>zsyncer</artifactId>
<version>-f69d844481-1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.4.1</version>
</dependency>
</dependencies>
<build>

View File

@ -3,7 +3,6 @@ package de.mc8051.arma3launcher;
import com.formdev.flatlaf.FlatDarkLaf;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import de.mc8051.arma3launcher.repo.RepositoryManger;
import de.mc8051.arma3launcher.steam.SteamTimer;
import org.ini4j.Ini;
@ -12,7 +11,6 @@ import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Locale;
import java.util.Timer;
@ -81,7 +79,7 @@ public class ArmA3Launcher {
frame.setLocationRelativeTo(null);
steamTimer.scheduleAtFixedRate(
new SteamTimer(gui),
new SteamTimer(),
500, // run first occurrence immediately
10000); // run every thirty seconds

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">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="1048" height="542"/>
<xy x="20" y="20" width="1048" height="526"/>
</constraints>
<properties/>
<border type="none"/>
@ -237,6 +237,7 @@
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<font size="18" style="1"/>
<margin top="15" left="0" bottom="15" right="0"/>
<text resource-bundle="lang" key="play_now"/>
@ -294,34 +295,73 @@
</component>
</children>
</grid>
<component id="113bf" class="javax.swing.JTree" binding="tree1" default-binding="true">
<constraints border-constraint="Center"/>
<properties/>
</component>
<grid id="74882" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="74882" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints border-constraint="South"/>
<properties/>
<border type="none"/>
<children>
<component id="f4a5" class="javax.swing.JButton" binding="allesAuswählenButton" default-binding="true">
<component id="13d22" class="javax.swing.JButton" binding="expandAllButton">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" 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="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="lang" key="select_all"/>
<enabled value="false"/>
<text resource-bundle="lang" key="expand_all"/>
</properties>
</component>
<component id="13d22" class="javax.swing.JButton" binding="allesAusklappenButton" default-binding="true">
<component id="43543" class="javax.swing.JComboBox" binding="comboBox1" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="2b206" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="lang" key="expand_all"/>
<text resource-bundle="lang" key="preset"/>
</properties>
</component>
<component id="c9d68" class="javax.swing.JButton" binding="refreshRepoButton">
<constraints>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="update_repository"/>
</properties>
</component>
<component id="4ec7b" class="javax.swing.JButton" binding="collapseAllButton">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="collapse_all"/>
</properties>
</component>
</children>
</grid>
<scrollpane id="56725" binding="updateTreeScrolPane">
<constraints border-constraint="Center"/>
<properties/>
<border type="none"/>
<children>
<grid id="44d39" binding="updateTreePanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints/>
<properties/>
<border type="none"/>
<children>
<component id="38143" class="javax.swing.JTree" binding="tree1" default-binding="true">
<constraints border-constraint="Center"/>
<properties/>
</component>
</children>
</grid>
</children>
</scrollpane>
</children>
</grid>
<grid id="dc84b" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
@ -367,7 +407,7 @@
</component>
</children>
</grid>
<grid id="3b45c" layout-manager="GridLayoutManager" row-count="4" 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="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="3" bottom="0" right="0"/>
<constraints border-constraint="Center"/>
<properties/>
@ -378,7 +418,7 @@
<grid row="0" 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 value="Label"/>
<text resource-bundle="lang" key="check_local_addons"/>
</properties>
</component>
<hspacer id="f47e0">
@ -386,27 +426,23 @@
<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"/>
</constraints>
</hspacer>
<vspacer id="5a5d2">
<constraints>
<grid row="3" 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="7b453" class="javax.swing.JLabel">
<component id="7b453" class="javax.swing.JLabel" binding="syncCheckStatusLabel">
<constraints>
<grid row="0" 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="Label"/>
<font style="2"/>
<text value=""/>
</properties>
</component>
<component id="c7b6" class="javax.swing.JProgressBar" binding="progressBar1" default-binding="true">
<component id="c7b6" class="javax.swing.JProgressBar" binding="syncCheckProgress">
<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"/>
</constraints>
<properties>
<string value=""/>
<stringPainted value="true"/>
<value value="50"/>
<value value="0"/>
</properties>
</component>
<grid id="c1d6e" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
@ -417,19 +453,21 @@
<properties/>
<border type="none"/>
<children>
<component id="c8876" class="javax.swing.JButton" binding="abbrechenButton" default-binding="true">
<component id="c8876" class="javax.swing.JButton" binding="syncCheckAbortButton">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="abort"/>
</properties>
</component>
<component id="d7e4b" class="javax.swing.JButton" binding="überprüfenButton" default-binding="true">
<component id="d7e4b" class="javax.swing.JButton" binding="syncCheckButton">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="check"/>
</properties>
</component>
@ -440,6 +478,54 @@
</hspacer>
</children>
</grid>
<component id="c97ef" class="javax.swing.JLabel">
<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"/>
</constraints>
<properties>
<text resource-bundle="lang" key="changed_files"/>
</properties>
</component>
<component id="22374" class="javax.swing.JLabel">
<constraints>
<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>
<properties>
<text resource-bundle="lang" key="added_files"/>
</properties>
</component>
<component id="e3c92" class="javax.swing.JLabel">
<constraints>
<grid row="5" 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="deleted_files"/>
</properties>
</component>
<component id="16f6a" class="javax.swing.JLabel" binding="syncChangedFilesLabel">
<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"/>
</constraints>
<properties>
<text value="0"/>
</properties>
</component>
<component id="929a5" class="javax.swing.JLabel" binding="syncAddedFilesLabel">
<constraints>
<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="0"/>
</properties>
</component>
<component id="f6ef3" class="javax.swing.JLabel" binding="syncDeletedFilesLabel">
<constraints>
<grid row="5" 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="0"/>
</properties>
</component>
</children>
</grid>
</children>
@ -516,7 +602,7 @@
<text resource-bundle="lang" key="remaining_time"/>
</properties>
</component>
<component id="b28b6" class="javax.swing.JLabel">
<component id="b28b6" class="javax.swing.JLabel" binding="syncSizeLabel">
<constraints>
<grid row="0" 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>
@ -613,27 +699,30 @@
<properties/>
<border type="none"/>
<children>
<component id="a5494" class="javax.swing.JButton" binding="downloadButton" default-binding="true">
<component id="a5494" class="javax.swing.JButton" binding="syncDownloadButton">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="download"/>
</properties>
</component>
<component id="f8be1" class="javax.swing.JButton" binding="abbrechenButton1" default-binding="true">
<component id="f8be1" class="javax.swing.JButton" binding="syncDownloadAbortButton">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="abort"/>
</properties>
</component>
<component id="c94f5" class="javax.swing.JButton" binding="pauseButton" default-binding="true">
<component id="c94f5" class="javax.swing.JButton" binding="syncPauseButton">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="pause"/>
</properties>
</component>
@ -690,6 +779,7 @@
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<text resource-bundle="lang" key="play"/>
</properties>
</component>
@ -1079,6 +1169,7 @@
</constraints>
<properties>
<text value="SkipIntro"/>
<toolTipText resource-bundle="lang" key="skipintro_desc"/>
</properties>
</component>
<component id="435dd" class="javax.swing.JLabel">

View File

@ -1,13 +1,19 @@
package de.mc8051.arma3launcher;
import de.mc8051.arma3launcher.interfaces.Observer;
import de.mc8051.arma3launcher.model.JCheckBoxTree;
import de.mc8051.arma3launcher.model.ModListRenderer;
import de.mc8051.arma3launcher.model.PresetListRenderer;
import de.mc8051.arma3launcher.model.PresetTableModel;
import de.mc8051.arma3launcher.model.ServerTableModel;
import de.mc8051.arma3launcher.objects.AbstractMod;
import de.mc8051.arma3launcher.objects.Mod;
import de.mc8051.arma3launcher.objects.ModFile;
import de.mc8051.arma3launcher.objects.Modset;
import de.mc8051.arma3launcher.objects.Server;
import de.mc8051.arma3launcher.repo.FileChecker;
import de.mc8051.arma3launcher.repo.RepositoryManger;
import de.mc8051.arma3launcher.steam.SteamTimer;
import de.mc8051.arma3launcher.utils.Callback;
import de.mc8051.arma3launcher.utils.LangUtils;
@ -16,6 +22,9 @@ import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.DefaultFormatter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@ -28,6 +37,11 @@ import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
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.Logger;
import java.util.stream.Stream;
@ -97,25 +111,50 @@ public class LauncherGUI implements Observer {
private JScrollPane settingScrollPane;
private JCheckBox settingsUseWorkshopBox;
private JTree tree1;
private JButton allesAuswählenButton;
private JButton allesAusklappenButton;
private JProgressBar progressBar1;
private JButton abbrechenButton;
private JButton überprüfenButton;
private JButton expandAllButton;
private JProgressBar syncCheckProgress;
private JButton syncCheckAbortButton;
private JButton syncCheckButton;
private JProgressBar progressBar2;
private JProgressBar progressBar3;
private JButton downloadButton;
private JButton abbrechenButton1;
private JButton pauseButton;
private JButton syncDownloadButton;
private JButton syncDownloadAbortButton;
private JButton syncPauseButton;
private JComboBox comboBox1;
private JButton refreshRepoButton;
private JPanel updateTreePanel;
private JScrollPane updateTreeScrolPane;
private JButton collapseAllButton;
private JLabel syncCheckStatusLabel;
private JLabel syncDeletedFilesLabel;
private JLabel syncAddedFilesLabel;
private JLabel syncChangedFilesLabel;
private JLabel syncSizeLabel;
private JCheckBoxTree repoTree;
private FileChecker fileChecker;
// TODO: Updater
/*
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() {
fileChecker = new FileChecker(syncCheckProgress);
RepositoryManger.getInstance().addObserver(this);
SteamTimer.addObserver(this);
fileChecker.addObserver(this);
updateTreePanel.remove(tree1);
repoTree = new JCheckBoxTree();
updateTreePanel.add(repoTree, BorderLayout.CENTER);
updateTreePanel.revalidate();
updateTreePanel.repaint();
tabbedPane1.setUI(new BasicTabbedPaneUI() {
private final Insets borderInsets = new Insets(0, 0, 0, 0);
@ -184,7 +223,7 @@ public class LauncherGUI implements Observer {
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);
removePresetButtom.setEnabled(false);
} else {
@ -226,8 +265,45 @@ public class LauncherGUI implements Observer {
});
settingScrollPane.getVerticalScrollBar().setUnitIncrement(16);
updateTreeScrolPane.getVerticalScrollBar().setUnitIncrement(16);
RepositoryManger.getInstance().refreshMeta();
refreshRepoButton.addActionListener(e -> RepositoryManger.getInstance().refreshModset());
expandAllButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repoTree.expandAllNodes();
}
});
collapseAllButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repoTree.collapseAllNodes();
}
});
new Thread(() -> {
RepositoryManger.getInstance().refreshMeta();
RepositoryManger.getInstance().refreshModset();
}).start();
syncCheckButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
syncCheckButton.setEnabled(false);
syncCheckAbortButton.setEnabled(true);
syncCheckStatusLabel.setText("Running!");
new Thread(() -> fileChecker.check()).start();
// TODO: disable JTree Checkboxes
}
});
syncCheckAbortButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileChecker.stop();
}
});
}
public static void infoBox(String infoMessage, String titleBar) {
@ -261,9 +337,53 @@ public class LauncherGUI implements Observer {
}
public void techCheck() {
// Arma Path set
// Steam running
// Arma not running
boolean pathSet = ArmA3Launcher.user_config.get("client").containsKey("armaPath") && ArmA3Launcher.user_config.get("client").containsKey("modPath");
if (SteamTimer.arma_running) {
playButton.setEnabled(false);
playPresetButton.setEnabled(false);
syncCheckButton.setEnabled(false);
refreshRepoButton.setEnabled(false);
playButton.setToolTipText(LangUtils.getInstance().getString("arma_running"));
playPresetButton.setToolTipText(LangUtils.getInstance().getString("arma_running"));
syncCheckButton.setToolTipText(LangUtils.getInstance().getString("arma_running"));
} else {
if (SteamTimer.steam_running) {
if (pathSet) {
playButton.setEnabled(true);
playPresetButton.setEnabled(true);
playButton.setToolTipText(null);
playPresetButton.setToolTipText(null);
} else {
playButton.setEnabled(false);
playPresetButton.setEnabled(false);
playButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
playPresetButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
}
} else {
playButton.setEnabled(false);
playPresetButton.setEnabled(false);
playButton.setToolTipText(LangUtils.getInstance().getString("steam_not_running"));
playPresetButton.setToolTipText(LangUtils.getInstance().getString("steam_not_running"));
}
if (pathSet) {
syncCheckButton.setEnabled(true);
refreshRepoButton.setEnabled(true);
syncCheckButton.setToolTipText(null);
refreshRepoButton.setToolTipText(null);
} else {
syncCheckButton.setEnabled(true);
refreshRepoButton.setEnabled(true);
syncCheckButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
refreshRepoButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
}
}
}
public boolean checkArmaPath(String path) {
@ -318,7 +438,15 @@ public class LauncherGUI implements Observer {
SwingUtilities.invokeLater(() -> warnBox(LangUtils.getInstance().getString("not_arma_dir_msg"), LangUtils.getInstance().getString("not_arma_dir")));
return false;
}
String modPath = ArmA3Launcher.user_config.get("client", "modPath");
if(sPath.equalsIgnoreCase(modPath)) {
SwingUtilities.invokeLater(() -> errorBox(LangUtils.getInstance().getString("same_mod_arma_dir_msg"), LangUtils.getInstance().getString("same_mod_arma_dir")));
return false;
}
settingsArmaPathText.setText(sPath);
techCheck();
return true;
}
});
@ -326,7 +454,16 @@ public class LauncherGUI implements Observer {
initFolderChooser(settingsModsPathText, settingsModsPathBtn, "modPath", Parameter.ParameterType.CLIENT, new Callback.JFileSelectCallback() {
@Override
public boolean allowSelection(File path) {
settingsModsPathText.setText(path.getAbsolutePath());
String sPath = path.getAbsolutePath();
String armaPath = ArmA3Launcher.user_config.get("client", "armaPath");
if(sPath.equalsIgnoreCase(armaPath)) {
SwingUtilities.invokeLater(() -> errorBox(LangUtils.getInstance().getString("same_mod_arma_dir_msg"), LangUtils.getInstance().getString("same_mod_arma_dir")));
return false;
}
settingsModsPathText.setText(sPath);
RepositoryManger.getInstance().refreshModset();
return true;
}
});
@ -413,9 +550,73 @@ public class LauncherGUI implements Observer {
spinner.addChangeListener(new SettingsHandler.SpinnerListener(paraObj));
}
public ArrayList<AbstractMod> getSyncList() {
ArrayList<AbstractMod> modList = new ArrayList<>();
HashMap<String, ArrayList<String>> tempMap = new HashMap<>();
for (TreePath checkedPath : repoTree.getCheckedPaths()) {
DefaultMutableTreeNode tn = (DefaultMutableTreeNode)checkedPath.getLastPathComponent();
if(tn.getChildCount() > 0) continue;
Object[] path = checkedPath.getPath();
DefaultMutableTreeNode[] modifiedArray = Arrays.stream(Arrays.copyOfRange(path, 1, path.length)).toArray(DefaultMutableTreeNode[]::new);
ArrayList<String> strings = new ArrayList<>();
if(tempMap.containsKey(String.valueOf(modifiedArray[0].getUserObject()))) {
strings = tempMap.get(String.valueOf(modifiedArray[0].getUserObject()));
}
String modPath = "";
for (int i = 1; i < modifiedArray.length; i++) {
modPath += String.valueOf(modifiedArray[i].getUserObject()) + "/";
}
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;
}
}
} 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) {
m.getFiles().remove(i);
}
}
modList.add(m);
}
}
}
}
return modList;
}
public void updateModList(Modset modset) {
ListModel<String> model = (ListModel)modList.getModel();
// TODO: RepositoryManger.downloadModlist
ListModel<String> model = (ListModel) modList.getModel();
// TODO: Show All Mods (keyname)
// TODO: Show not installed Mods with red font
// TODO: Select Mod if in modset.Mods
@ -423,27 +624,184 @@ public class LauncherGUI implements Observer {
// TODO: Wenn modset.type == Server alle Checkboxen deaktivieren!
}
public void updateRepoTree() {
expandAllButton.setEnabled(false);
collapseAllButton.setEnabled(false);
DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
root.setUserObject("Repository");
root.removeAllChildren();
for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
if (abstractMod instanceof Mod) {
// Whole Folder
// TODO: Recursives Ordner Parsen und einzelne Treenodes erstellen
Mod m = (Mod) abstractMod;
DefaultMutableTreeNode modFolder = new DefaultMutableTreeNode(m.getName(), true);
model.insertNodeInto(modFolder, root, root.getChildCount());
for (ModFile modfile : m.getFiles()) {
DefaultMutableTreeNode lastNode = modFolder;
ArrayList<String> path = modfile.getPath();
for (int i = 0; i < path.size(); i++) {
boolean found = false;
for (int j = 0; j < lastNode.getChildCount(); j++) {
DefaultMutableTreeNode temp = (DefaultMutableTreeNode) lastNode.getChildAt(j);
if (temp.getUserObject().equals(path.get(i))) {
found = true;
lastNode = temp;
break;
}
}
if (!found) {
DefaultMutableTreeNode temp = new DefaultMutableTreeNode(path.get(i));
model.insertNodeInto(temp, lastNode, lastNode.getChildCount());
lastNode = temp;
}
}
model.insertNodeInto(new DefaultMutableTreeNode(modfile.getName()), lastNode, lastNode.getChildCount());
}
sort(modFolder);
} else if (abstractMod instanceof ModFile) {
// Just a Single FIle
ModFile m = (ModFile) abstractMod;
model.insertNodeInto(new DefaultMutableTreeNode(m.getName(), false), root, root.getChildCount());
}
}
sort(root);
repoTree.clearCheckChangeEventListeners();
repoTree.resetCheckingState();
SwingUtilities.invokeLater(() -> {
model.nodeChanged(root);
model.reload();
repoTree.revalidate();
repoTree.repaint();
updateTreePanel.revalidate();
updateTreePanel.repaint();
});
expandAllButton.setEnabled(true);
collapseAllButton.setEnabled(true);
}
public DefaultMutableTreeNode sort(DefaultMutableTreeNode node) {
//sort alphabetically
for(int i = 0; i < node.getChildCount() - 1; i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
String nt = child.getUserObject().toString();
for(int j = i + 1; j <= node.getChildCount() - 1; j++) {
DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
String np = prevNode.getUserObject().toString();
if(nt.compareToIgnoreCase(np) > 0) {
node.insert(child, j);
node.insert(prevNode, i);
}
}
if(child.getChildCount() > 0) {
sort(child);
}
}
//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++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
for(int j = i + 1; j <= node.getChildCount() - 1; j++) {
DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
if(!prevNode.isLeaf() && child.isLeaf()) {
node.insert(child, j);
node.insert(prevNode, i);
}
}
}
return node;
}
@Override
public void update(Object o) {
String s = String.valueOf(o);
public void update(String s) {
System.out.println(s);
if (s.equals(RepositoryManger.Type.METADATA.toString())) {
switch (RepositoryManger.getInstance().getStatus(RepositoryManger.Type.METADATA)) {
case ERROR:
errorBox("Metadata download failed. Is the server availaible? Do you have an active internet connection?", "Download failed");
System.exit(1);
break;
if (s.equals("refreshMeta")) {
case FINNISHED:
SwingUtilities.invokeLater(() -> {
ServerTableModel model = (ServerTableModel) serverTable.getModel();
Server.SERVER_LIST.forEach((name, server) -> model.add(server));
});
SwingUtilities.invokeLater(() -> {
PresetTableModel model = (PresetTableModel) presetList.getModel();
model.clear();
model.add(new Modset("--Server", Modset.Type.CLIENT, null, false));
Modset.MODSET_LIST.forEach((name, set) -> {
model.add(set);
});
});
break;
}
} else if (s.equals("steamtimer")) {
SwingUtilities.invokeLater(() -> {
ServerTableModel model = (ServerTableModel) serverTable.getModel();
Server.SERVER_LIST.forEach((name, server) -> model.add(server));
updateLabels(SteamTimer.steam_running, SteamTimer.arma_running);
techCheck();
});
} else if (s.equals(RepositoryManger.Type.MODSET.toString())) {
switch (RepositoryManger.getInstance().getStatus(RepositoryManger.Type.METADATA)) {
case FINNISHED:
refreshRepoButton.setEnabled(true);
updateRepoTree();
break;
SwingUtilities.invokeLater(() -> {
PresetTableModel model = (PresetTableModel) presetList.getModel();
model.clear();
case RUNNING:
refreshRepoButton.setEnabled(false);
break;
}
} else if(s.equals("fileChecker")) {
syncCheckButton.setEnabled(true);
syncCheckAbortButton.setEnabled(false);
syncCheckStatusLabel.setText("Finished!");
updateRepoTree();
// TODO: Label einfärben
// TODO: Enable Tree Checkboxes
syncDownloadButton.setEnabled(true);
syncAddedFilesLabel.setText(String.valueOf(fileChecker.getAddedCount()));
syncChangedFilesLabel.setText(String.valueOf(fileChecker.getChangedCount()));
syncDeletedFilesLabel.setText(String.valueOf(fileChecker.getDeletedCount()));
model.add(new Modset("--Server", Modset.Type.CLIENT, null, false));
syncSizeLabel.setText(String.valueOf(fileChecker.getSize())); // TODO: Make Humanreadable
} else if (s.equals("fileCheckerStopped")) {
syncCheckButton.setEnabled(true);
syncCheckAbortButton.setEnabled(false);
syncCheckProgress.setValue(0);
syncCheckStatusLabel.setText("Failed!");
Modset.MODSET_LIST.forEach((name, set) -> {
model.add(set);
});
});
syncAddedFilesLabel.setText("" + 0);
syncChangedFilesLabel.setText("" + 0);
syncDeletedFilesLabel.setText("" + 0);
syncSizeLabel.setText("0.0 B");
}
}
}

View File

@ -7,5 +7,5 @@ public interface Observable {
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers(Object obj);
public void notifyObservers(String obj);
}

View File

@ -5,5 +5,5 @@ package de.mc8051.arma3launcher.interfaces;
*/
public interface Observer {
public void update(Object o);
public void update(String o);
}

View File

@ -0,0 +1,295 @@
package de.mc8051.arma3launcher.model;
import javax.swing.*;
import javax.swing.event.EventListenerList;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EventListener;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
/**
* Created by SomethingSomething https://stackoverflow.com/a/21851201/5605489
*/
public class JCheckBoxTree extends JTree {
private static final long serialVersionUID = -4194122328392241790L;
JCheckBoxTree selfPointer = this;
// Defining data structure that will enable to fast check-indicate the state of each node
// It totally replaces the "selection" mechanism of the JTree
private class CheckedNode {
boolean isSelected;
boolean hasChildren;
boolean allChildrenSelected;
public CheckedNode(boolean isSelected_, boolean hasChildren_, boolean allChildrenSelected_) {
isSelected = isSelected_;
hasChildren = hasChildren_;
allChildrenSelected = allChildrenSelected_;
}
}
HashMap<TreePath, CheckedNode> nodesCheckingState;
HashSet<TreePath> checkedPaths = new HashSet<TreePath>();
// Defining a new event type for the checking mechanism and preparing event-handling mechanism
protected EventListenerList listenerList = new EventListenerList();
public class CheckChangeEvent extends EventObject {
private static final long serialVersionUID = -8100230309044193368L;
public CheckChangeEvent(Object source) {
super(source);
}
}
public interface CheckChangeEventListener extends EventListener {
public void checkStateChanged(CheckChangeEvent event);
}
public void addCheckChangeEventListener(CheckChangeEventListener listener) {
listenerList.add(CheckChangeEventListener.class, listener);
}
public void removeCheckChangeEventListener(CheckChangeEventListener listener) {
listenerList.remove(CheckChangeEventListener.class, listener);
}
public void clearCheckChangeEventListeners() {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] == CheckChangeEventListener.class) {
listenerList.remove(CheckChangeEventListener.class, ((CheckChangeEventListener) listeners[i + 1]));
}
}
}
void fireCheckChangeEvent(CheckChangeEvent evt) {
Object[] listeners = listenerList.getListenerList();
for (int i = 0; i < listeners.length; i++) {
if (listeners[i] == CheckChangeEventListener.class) {
((CheckChangeEventListener) listeners[i + 1]).checkStateChanged(evt);
}
}
}
// Override
public void setModel(TreeModel newModel) {
super.setModel(newModel);
resetCheckingState();
}
public void createModel(Object[] value) {
createTreeModel(value);
}
// New method that returns only the checked paths (totally ignores original "selection" mechanism)
public TreePath[] getCheckedPaths() {
return checkedPaths.toArray(new TreePath[checkedPaths.size()]);
}
// Returns true in case that the node is selected, has children but not all of them are selected
public boolean isSelectedPartially(TreePath path) {
CheckedNode cn = nodesCheckingState.get(path);
return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected;
}
public void resetCheckingState() {
nodesCheckingState = new HashMap<TreePath, CheckedNode>();
checkedPaths = new HashSet<TreePath>();
DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
if (node == null) {
return;
}
addSubtreeToCheckingStateTracking(node);
}
// Creating data structure of the current model for the checking mechanism
private void addSubtreeToCheckingStateTracking(DefaultMutableTreeNode node) {
TreeNode[] path = node.getPath();
TreePath tp = new TreePath(path);
CheckedNode cn = new CheckedNode(false, node.getChildCount() > 0, false);
nodesCheckingState.put(tp, cn);
for (int i = 0; i < node.getChildCount(); i++) {
addSubtreeToCheckingStateTracking((DefaultMutableTreeNode) tp.pathByAddingChild(node.getChildAt(i)).getLastPathComponent());
}
}
// Overriding cell renderer by a class that ignores the original "selection" mechanism
// It decides how to show the nodes due to the checking-mechanism
private class CheckBoxCellRenderer extends JPanel implements TreeCellRenderer {
private static final long serialVersionUID = -7341833835878991719L;
JCheckBox checkBox;
public CheckBoxCellRenderer() {
super();
this.setLayout(new BorderLayout());
checkBox = new JCheckBox();
add(checkBox, BorderLayout.CENTER);
setOpaque(false);
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
checkBox.setText(value.toString());
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
TreePath tp = new TreePath(node.getPath());
CheckedNode cn = nodesCheckingState.get(tp);
if (cn == null) {
return this;
}
checkBox.setSelected(cn.isSelected);
checkBox.setOpaque(cn.isSelected && cn.hasChildren && !cn.allChildrenSelected);
return this;
}
}
public JCheckBoxTree() {
super();
// Disabling toggling by double-click
this.setToggleClickCount(0);
// Overriding cell renderer by new one defined above
CheckBoxCellRenderer cellRenderer = new CheckBoxCellRenderer();
this.setCellRenderer(cellRenderer);
// Overriding selection model by an empty one
DefaultTreeSelectionModel dtsm = new DefaultTreeSelectionModel() {
private static final long serialVersionUID = -8190634240451667286L;
// Totally disabling the selection mechanism
public void setSelectionPath(TreePath path) {
}
public void addSelectionPath(TreePath path) {
}
public void removeSelectionPath(TreePath path) {
}
public void setSelectionPaths(TreePath[] pPaths) {
}
};
// Calling checking mechanism on mouse click
this.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
TreePath tp = selfPointer.getPathForLocation(arg0.getX(), arg0.getY());
if (tp == null) {
return;
}
boolean checkMode = !nodesCheckingState.get(tp).isSelected;
checkSubTree(tp, checkMode);
updatePredecessorsWithCheckMode(tp, checkMode);
// Firing the check change event
fireCheckChangeEvent(new CheckChangeEvent(new Object()));
// Repainting tree after the data structures were updated
selfPointer.repaint();
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
}
});
this.setSelectionModel(dtsm);
}
// When a node is checked/unchecked, updating the states of the predecessors
protected void updatePredecessorsWithCheckMode(TreePath tp, boolean check) {
TreePath parentPath = tp.getParentPath();
// If it is the root, stop the recursive calls and return
if (parentPath == null) {
return;
}
CheckedNode parentCheckedNode = nodesCheckingState.get(parentPath);
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parentPath.getLastPathComponent();
parentCheckedNode.allChildrenSelected = true;
parentCheckedNode.isSelected = false;
for (int i = 0; i < parentNode.getChildCount(); i++) {
TreePath childPath = parentPath.pathByAddingChild(parentNode.getChildAt(i));
CheckedNode childCheckedNode = nodesCheckingState.get(childPath);
// It is enough that even one subtree is not fully selected
// to determine that the parent is not fully selected
if (!childCheckedNode.allChildrenSelected) {
parentCheckedNode.allChildrenSelected = false;
}
// If at least one child is selected, selecting also the parent
if (childCheckedNode.isSelected) {
parentCheckedNode.isSelected = true;
}
}
if (parentCheckedNode.isSelected) {
checkedPaths.add(parentPath);
} else {
checkedPaths.remove(parentPath);
}
// Go to upper predecessor
updatePredecessorsWithCheckMode(parentPath, check);
}
// Recursively checks/unchecks a subtree
protected void checkSubTree(TreePath tp, boolean check) {
CheckedNode cn = nodesCheckingState.get(tp);
cn.isSelected = check;
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp.getLastPathComponent();
for (int i = 0; i < node.getChildCount(); i++) {
checkSubTree(tp.pathByAddingChild(node.getChildAt(i)), check);
}
cn.allChildrenSelected = check;
if (check) {
checkedPaths.add(tp);
} else {
checkedPaths.remove(tp);
}
}
public void expandAllNodes() {
setTreeExpandedState(true);
}
public void collapseAllNodes() {
setTreeExpandedState(false);
}
private void setTreeExpandedState(boolean expanded) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
setNodeExpandedState( node, expanded);
}
private void setNodeExpandedState(DefaultMutableTreeNode node, boolean expanded) {
ArrayList<TreeNode> list = Collections.list(node.children());
for (TreeNode treeNode : list) {
setNodeExpandedState((DefaultMutableTreeNode)treeNode, expanded);
}
if (!expanded && node.isRoot()) {
return;
}
TreePath path = new TreePath(node.getPath());
if (expanded) {
expandPath(path);
} else {
collapsePath(path);
}
}
}

View File

@ -0,0 +1,9 @@
package de.mc8051.arma3launcher.objects;
/**
* Created by gurkengewuerz.de on 25.03.2020.
*/
public interface AbstractMod {
public String getName();
}

View File

@ -1,17 +1,40 @@
package de.mc8051.arma3launcher.objects;
import java.util.ArrayList;
/**
* Created by gurkengewuerz.de on 25.03.2020.
*/
public class Mod {
public class Mod implements AbstractMod {
private String name;
private long size;
private ArrayList<ModFile> files;
public Mod(String name) {
this(name, -1, new ArrayList<>());
}
public Mod(String name, long size, ArrayList<ModFile> files) {
this.name = name;
this.size = size;
this.files = files;
}
public ArrayList<ModFile> getFiles() {
return files;
}
public long getSize() {
return size;
}
public String getName() {
return name;
}
public Mod clone() {
return new Mod(name, size, new ArrayList<>(files));
}
}

View File

@ -0,0 +1,82 @@
package de.mc8051.arma3launcher.objects;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
/**
* Created by gurkengewuerz.de on 25.03.2020.
*/
public class ModFile implements AbstractMod {
private File f;
private long size;
private String folder;
private String filename;
private String extension;
private String modfileString;
public ModFile(File f, String modfile, long size) {
// File: Abosolut Path
// modfile: addons/config/something.pbo
// size: size as in metafile on server
this.f = f;
this.size = size;
this.folder = FilenameUtils.getPath(modfile);
this.filename = FilenameUtils.getBaseName(modfile);
this.extension = FilenameUtils.getExtension(modfile);
this.modfileString = modfile;
}
public long getSize() {
return size;
}
public String getReletaivePath() {
return folder;
}
public String getFilename() {
return filename;
}
public String getExtension() {
return extension;
}
public ArrayList<String> getPath() {
ArrayList<String> list = new ArrayList<>();
File relativePath = new File("./"+ modfileString);
do {
list.add(relativePath.getName());
relativePath = relativePath.getParentFile();
} while (relativePath.getParentFile() != null);
list.remove(0);
Collections.reverse(list);
return list;
}
public long getLocalSize() {
if(!f.exists() || !f.isFile()) return -1;
return f.length();
}
public boolean exists() {
if(!f.exists() || !f.isFile()) return false;
return true;
}
public String getName() {
return filename + (extension.equals("") ? "" : "." + extension);
}
public String getModfileString() {
return modfileString;
}
public File getLocaleFile() {
return f;
}
}

View File

@ -56,6 +56,10 @@ public class Modset {
return type;
}
public void play() {
// TODO: Implement play with this Modset
}
public static enum Type {
SERVER,
CLIENT

View File

@ -0,0 +1,10 @@
package de.mc8051.arma3launcher.repo;
/**
* Created by gurkengewuerz.de on 25.03.2020.
*/
public enum DownloadStatus {
RUNNING,
FINNISHED,
ERROR;
}

View File

@ -0,0 +1,203 @@
package de.mc8051.arma3launcher.repo;
import de.mc8051.arma3launcher.ArmA3Launcher;
import de.mc8051.arma3launcher.interfaces.Observable;
import de.mc8051.arma3launcher.interfaces.Observer;
import de.mc8051.arma3launcher.objects.AbstractMod;
import de.mc8051.arma3launcher.objects.Mod;
import de.mc8051.arma3launcher.objects.ModFile;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Created by gurkengewuerz.de on 26.03.2020.
*/
public class FileChecker implements Observable {
private List<Observer> observerList = new ArrayList<>();
private JProgressBar pb;
private boolean stop = false;
ArrayList<Path> deleted = new ArrayList<>();
HashMap<String, ArrayList<ModFile>> changed = new HashMap<>();
int changedCount = 0;
HashMap<String, ArrayList<ModFile>> added = new HashMap<>();
int addedCount = 0;
long size = 0;
public FileChecker(JProgressBar pb) {
this.pb = pb;
}
public void check() {
deleted.clear();
changed.clear();
changedCount = 0;
added.clear();
addedCount = 0;
size = 0;
int i = 0;
SwingUtilities.invokeLater(() -> {
pb.setMaximum(RepositoryManger.MOD_LIST_SIZE);
pb.setValue(0);
});
for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
if(stop) {
stop = false;
notifyObservers("fileCheckerStopped");
return;
}
if(abstractMod instanceof Mod) {
Mod m = (Mod) abstractMod;
for (ModFile mf : m.getFiles()) {
checkFile(mf.getName(), mf);
i++;
int finalI = i;
SwingUtilities.invokeLater(() -> {
pb.setValue(finalI);
});
if(stop) {
stop = false;
notifyObservers("fileCheckerStopped");
return;
}
}
} else if (abstractMod instanceof ModFile) {
ModFile mf = (ModFile) abstractMod;
checkFile(mf.getName(), mf);
i++;
int finalI1 = i;
SwingUtilities.invokeLater(() -> {
pb.setValue(finalI1);
});
}
}
checkDeleted();
notifyObservers("fileChecker");
}
public void stop() {
stop = true;
}
private void checkFile(String mod, ModFile mf) {
// TODO: Add mf to Array if Array already exists
if(!mf.exists()) {
added.put(mod, mf);
addedCount++;
size += mf.getSize();
return;
}
if(mf.getLocalSize() != mf.getSize()) {
changed.put(mod, mf);
changedCount++;
size += mf.getSize();
return;
}
}
private void checkDeleted() {
String modPath = ArmA3Launcher.user_config.get("client", "modPath");
if(modPath == null) modPath = "";
try {
List<Path> filePathList = Files.find(Paths.get(modPath),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.collect(Collectors.toList());
for (Path localPath : filePathList) {
ModFile deleteable = null;
outerloop:
for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
if(abstractMod instanceof Mod) {
Mod m = (Mod) abstractMod;
for (ModFile mf : m.getFiles()) {
if (mf.getLocaleFile().getPath().equals(localPath.toString())) {
deleteable = mf;
break outerloop;
}
}
} else if (abstractMod instanceof ModFile) {
ModFile mf = (ModFile) abstractMod;
if (mf.getLocaleFile().getPath().equals(localPath.toString())) {
deleteable = mf;
break outerloop;
}
}
}
if (deleteable == null) {
deleted.add(localPath);
}
}
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
}
public ArrayList<Path> getDeleted() {
return deleted;
}
public HashMap<String, ArrayList<ModFile>> getChanged() {
return changed;
}
public HashMap<String, ArrayList<ModFile>> getAdded() {
return added;
}
public int getDeletedCount() {
return deleted.size();
}
public int getChangedCount() {
return changedCount;
}
public int getAddedCount() {
return addedCount;
}
public long getSize() {
return size;
}
@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

@ -3,6 +3,9 @@ package de.mc8051.arma3launcher.repo;
import de.mc8051.arma3launcher.ArmA3Launcher;
import de.mc8051.arma3launcher.interfaces.Observable;
import de.mc8051.arma3launcher.interfaces.Observer;
import de.mc8051.arma3launcher.objects.AbstractMod;
import de.mc8051.arma3launcher.objects.Mod;
import de.mc8051.arma3launcher.objects.ModFile;
import de.mc8051.arma3launcher.objects.Modset;
import de.mc8051.arma3launcher.objects.Server;
import de.mc8051.arma3launcher.utils.Callback;
@ -12,8 +15,11 @@ import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -24,19 +30,49 @@ import java.util.logging.Logger;
public class RepositoryManger implements Observable {
private static RepositoryManger instance;
public static ArrayList<AbstractMod> MOD_LIST = new ArrayList<>();
public static int MOD_LIST_SIZE = 0;
private static HashMap<Type, DownloadStatus> statusMap = new HashMap<>();
private List<Observer> observerList = new ArrayList<>();
private OkHttpClient client = new OkHttpClient();
private RepositoryManger() {
statusMap.put(Type.METADATA, DownloadStatus.FINNISHED);
statusMap.put(Type.MODSET, DownloadStatus.FINNISHED);
}
private void getAsync(String url, Callback.HttpCallback callback) {
new Thread(() -> {
try {
Request request = new Request.Builder()
.url(url)
.build();
Response r = client.newCall(request).execute();
if (!r.isSuccessful()) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Cant open " + r.request().url().toString() + " code " + r.code());
return;
}
callback.response(r);
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
callback.response(null);
}
}).start();
}
public void refreshMeta() {
downloadMeta(new Callback.HttpCallback() {
statusMap.replace(Type.METADATA, DownloadStatus.RUNNING);
RepositoryManger.getInstance().notifyObservers(Type.METADATA.toString());
getAsync(ArmA3Launcher.config.getString("sync.url") + "/.sync/server.json", new Callback.HttpCallback() {
@Override
public void response(Response r) {
if (!r.isSuccessful()) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Cant open " + r.request().url().toString() + " code " + r.code());
RepositoryManger.getInstance().notifyObservers("refreshMetaFailed");
statusMap.replace(Type.METADATA, DownloadStatus.ERROR);
RepositoryManger.getInstance().notifyObservers(Type.METADATA.toString());
return;
}
@ -44,6 +80,7 @@ public class RepositoryManger implements Observable {
JSONObject jsonObject = new JSONObject(r.body().string());
if (jsonObject.has("modsets")) {
Modset.MODSET_LIST.clear();
JSONArray modsets = jsonObject.getJSONArray("modsets");
if (modsets.length() > 0) {
for (int i = 0; i < modsets.length(); i++) {
@ -55,6 +92,7 @@ public class RepositoryManger implements Observable {
// Init servers after modsets because server search preset string in modsets
if (jsonObject.has("servers")) {
Server.SERVER_LIST.clear();
JSONArray servers = jsonObject.getJSONArray("servers");
if (servers.length() > 0) {
for (int i = 0; i < servers.length(); i++) {
@ -64,7 +102,8 @@ public class RepositoryManger implements Observable {
}
}
RepositoryManger.getInstance().notifyObservers("refreshMeta");
statusMap.replace(Type.METADATA, DownloadStatus.FINNISHED);
RepositoryManger.getInstance().notifyObservers(Type.METADATA.toString());
} catch (IOException | NullPointerException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
@ -72,19 +111,68 @@ public class RepositoryManger implements Observable {
});
}
private void downloadMeta(Callback.HttpCallback callback) {
new Thread(() -> {
try {
Request request = new Request.Builder()
.url(ArmA3Launcher.config.getString("sync.url") + "/.sync/server.json")
.build();
public void refreshModset() {
statusMap.replace(Type.MODSET, DownloadStatus.RUNNING);
RepositoryManger.getInstance().notifyObservers(Type.MODSET.toString());
getAsync(ArmA3Launcher.config.getString("sync.url") + "/.sync/modset.json", new Callback.HttpCallback() {
@Override
public void response(Response r) {
if (!r.isSuccessful()) {
statusMap.replace(Type.MODSET, DownloadStatus.ERROR);
RepositoryManger.getInstance().notifyObservers(Type.MODSET.toString());
return;
}
callback.response(client.newCall(request).execute());
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
callback.response(null);
try {
RepositoryManger.MOD_LIST.clear();
RepositoryManger.MOD_LIST_SIZE = 0;
JSONObject jsonObject = new JSONObject(r.body().string());
String modPath = ArmA3Launcher.user_config.get("client", "modPath");
if(modPath == null) modPath = "";
String finalModPath = modPath;
jsonObject.keySet().forEach(modname ->
{
Object keyvalue = jsonObject.get(modname);
if (!(keyvalue instanceof JSONObject)) return;
JSONObject jsonMod = (JSONObject)keyvalue;
if(!jsonMod.has("size")) return;
long modsize = jsonMod.getLong("size");
if(jsonMod.has("content")) {
// Mod Directory
JSONObject content = jsonMod.getJSONObject("content");
ArrayList<ModFile> modFiles = new ArrayList<>();
Iterator<String> keys = content.keys();
while (keys.hasNext()) {
String modfile = keys.next();
long modfilesize = content.getLong(modfile);
modFiles.add(new ModFile(new File(finalModPath + File.separator + modname + File.separator + modfile), modfile, modfilesize));
RepositoryManger.MOD_LIST_SIZE++;
}
MOD_LIST.add(new Mod(modname, modsize, modFiles));
} else {
// Single File
MOD_LIST.add(new ModFile(new File(finalModPath + File.separator + modname), modname, modsize));
RepositoryManger.MOD_LIST_SIZE++;
}
});
statusMap.replace(Type.MODSET, DownloadStatus.FINNISHED);
RepositoryManger.getInstance().notifyObservers(Type.MODSET.toString());
} catch (IOException | NullPointerException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
}
}).start();
});
}
public static RepositoryManger getInstance() {
@ -92,6 +180,10 @@ public class RepositoryManger implements Observable {
return instance;
}
public DownloadStatus getStatus(Type type) {
return statusMap.get(type);
}
@Override
public void addObserver(Observer observer) {
observerList.add(observer);
@ -103,7 +195,24 @@ public class RepositoryManger implements Observable {
}
@Override
public void notifyObservers(Object obj) {
public void notifyObservers(String obj) {
for (Observer obs : observerList) obs.update(obj);
}
public enum Type {
METADATA("metadata"),
MODSET("modset");
private String modset;
Type(String modset) {
this.modset = modset;
}
@Override
public String toString() {
return modset;
}
}
}

View File

@ -0,0 +1,8 @@
package de.mc8051.arma3launcher.repo;
/**
* Created by gurkengewuerz.de on 25.03.2020.
*/
public class Syncer {
// FilenameUtils.directoryContains
}

View File

@ -2,10 +2,12 @@ package de.mc8051.arma3launcher.steam;
import de.mc8051.arma3launcher.LauncherGUI;
import de.mc8051.arma3launcher.SteamUtils;
import de.mc8051.arma3launcher.interfaces.Observer;
import de.ralleytn.simple.registry.Key;
import de.ralleytn.simple.registry.Registry;
import java.io.IOException;
import java.util.ArrayList;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -15,24 +17,22 @@ import java.util.logging.Logger;
*/
public class SteamTimer extends TimerTask {
private static ArrayList<Observer> observers = new ArrayList<>();
public static boolean steam_running = false;
public static boolean arma_running = false;
private LauncherGUI gui;
public SteamTimer(LauncherGUI gui) {
this.gui = gui;
}
@Override
public void run() {
String OS = System.getProperty("os.name").toUpperCase();
if (!OS.contains("WIN")) return;
boolean old_steamrunning = steam_running;
boolean old_arma_running = arma_running;
try {
if(!SteamUtils.findProcess("steam.exe")) {
steam_running = false;
gui.updateLabels(steam_running, arma_running);
if(old_steamrunning != steam_running) notifyObservers("steamtimer");
return;
}
@ -42,19 +42,28 @@ public class SteamTimer extends TimerTask {
if(activeSteamUser.equals("0x0")) {
steam_running = false;
gui.updateLabels(steam_running, arma_running);
if(old_steamrunning != steam_running) notifyObservers("steamtimer");
return;
}
steam_running = true;
arma_running = SteamUtils.findProcess("arma3.exe") || SteamUtils.findProcess("arma3_x64.exe") || SteamUtils.findProcess("arma3launcher.exe");
if(old_steamrunning != steam_running || old_arma_running != arma_running) notifyObservers("steamtimer");
} catch (IOException e) {
steam_running = false;
arma_running = false;
notifyObservers("steamtimer");
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
}
gui.updateLabels(steam_running, arma_running);
public static void addObserver(Observer observer) {
observers.add(observer);
}
public void notifyObservers(String obj) {
for(Observer o : observers) o.update(obj);
}
}

View File

@ -1,3 +1,4 @@
arma_running=ArmA läuft noch. Bitte beende ArmA.
abort=Abbrechen
arm33_parameter=ArmA 3 Startparameter
arma3_installpath=ArmA 3 Installationspfad
@ -65,7 +66,7 @@ showscripterrors_desc=Fehler in Scripten werden in einem Hinweistext signalisier
signed_in=eingeloggt
speed=Geschwindigkeit
speed_up_game=Spielstart beschleunigen
spikintro_desc=Die Intro-Sequenz wird übersprungen.
skipintro_desc=Die Intro-Sequenz wird übersprungen.
total_file_size=Gesamtgröße
update=Update
use64bitclient_desc=Startet Arma3 mit der für 64-Bit Betriebssysteme optimierten .exe-Datei (kann die Performance von Arma 3 verbessern und Probleme beheben (bspw. 3-FPS-Bug).
@ -80,3 +81,13 @@ du sie deaktivieren und ohne diese Option erneut syncen.\
Ebenfalls könnte es zu kurzen Performance einbußen kommen.
window_desc=Ist diese Option aktiv, wird Arma 3 im Fenstermodus gestartet.
world_desc=Hier kann eine Karte eingetragen werden, die geladen und in den Menüs als Hintergrund angezeigt werden soll (z.B. „altis“ oder „stratis“ ohne Anführungszeichen!). Ist das Feld leer, wird keine Karte während des Startens geladen und der Start von Arma 3 ist entsprechend schneller.
path_not_set=ArmA oder Mod Verzeichnis nicht gesetzt
steam_not_running=Steam läuft nicht. Bitte starte Steam.
update_repository=Repository aktualisieren
collapse_all=Alles einklappen
same_mod_arma_dir_msg=Das ArmA sowie Mod Verzeichnis dürfen nicht identisch sein.
same_mod_arma_dir=Gleiches Verzeichnis
check_local_addons=Lokale Dateien überprüfen
changed_files=Veränderte Datien
added_files=Hinzugefügte Datien
deleted_files=Gelöschte Dateien

View File

@ -1,6 +1,7 @@
abort=Abort
arm33_parameter=ArmA 3 Start parameters
arma3_installpath=ArmA 3 Installation path
arma_running=ArmA is still running. Please exit ArmA.
backend_url=Backend URL
behaviour_aafter_start=Behaviour after start
beta_desc=Data from subdirectories can be started as well.
@ -64,7 +65,7 @@ showscripterrors_desc=Errors in scripts are signaled in a message text.
signed_in=signed in
speed=Speed
speed_up_game=Speed up game start
spikintro_desc=The intro sequence is skipped.
skipintro_desc=The intro sequence is skipped.
total_file_size=Total file size
update=Update
use64bitclient_desc=Starts Arma3 with the .exe file optimized for 64-bit operating systems (can improve the performance of Arma 3 and fix problems (e.g. 3-FPS bug)
@ -79,3 +80,9 @@ you deactivate it and sync again without this option.\
This could also lead to short performance losses.
window_desc=If this option is active, Arma 3 is started in windowed mode.
world_desc=Here you can enter a card to be loaded and displayed as background in the menus (e.g. "altis" or "stratis" - without quotation marks!). If the field is empty, no map will be loaded during startup and the start of Arma 3 will be faster.
path_not_set=ArmA or Mod directory not set
steam_not_running=Steam not running. Please start Steam.
repository_content=Repository Content
collapse_all=Collapse All
same_mod_arma_dir_msg=The ArmA and Mod directory must not be identical.
same_mod_arma_dir=Same directory