diff --git a/src/main/java/com/osiris/autoplug/client/configs/ModsConfig.java b/src/main/java/com/osiris/autoplug/client/configs/ModsConfig.java index 6a9870ab..91cd45af 100644 --- a/src/main/java/com/osiris/autoplug/client/configs/ModsConfig.java +++ b/src/main/java/com/osiris/autoplug/client/configs/ModsConfig.java @@ -44,6 +44,7 @@ public ModsConfig() throws IOException, DuplicateKeyException, YamlReaderExcepti "If there are mods that weren't found by the search-algorithm, you can add an id (spigot or bukkit) and a custom link (optional & must be a static link to the latest mod jar).\n" + "modrinth-id: Is the 'Project-ID' and can be found on the mods modrinth site inside of the 'About' box, under 'Technical Information' at the bottom left.\n" + "curseforge-id: Is also called 'Project-ID' and can be found on the mods curseforge site inside of the 'About' box at the right.\n" + + "steam-workshop-id: The Steam Workshop item id read from the mods metadata. Requires server-updater.software to be a numeric Steam app-id.\n" + "ignore-content-type: If true, does not check if the downloaded file is of type jar or zip, and downloads it anyway.\n" + "force-latest: If true, does not search for updates compatible with this Minecraft version and simply picks the latest release.\n" + "force-update: If true, downloads the update every time even if its already on the latest version.\n" + diff --git a/src/main/java/com/osiris/autoplug/client/configs/UpdaterConfig.java b/src/main/java/com/osiris/autoplug/client/configs/UpdaterConfig.java index 45437a41..6c815a82 100644 --- a/src/main/java/com/osiris/autoplug/client/configs/UpdaterConfig.java +++ b/src/main/java/com/osiris/autoplug/client/configs/UpdaterConfig.java @@ -230,6 +230,9 @@ public UpdaterConfig() throws IOException, DuplicateKeyException, YamlReaderExce mods_update_check_name_for_mod_loader = put(name, "mods-updater", "check-name-for-mod-loader").setDefValues("false").setComments( "Only relevant for determining if a curseforge mod release is forge or fabric.", "If enabled additionally checks the mod name to see if it contains fabric or forge."); + mods_updater_path.setComments( + "Path to your mods folder.", + "Steam Workshop mods with supported metadata can be updated through SteamCMD when server-updater.software is set to a numeric Steam app-id."); save(); unlockFile(); diff --git a/src/main/java/com/osiris/autoplug/client/tasks/updater/mods/SteamWorkshopMod.java b/src/main/java/com/osiris/autoplug/client/tasks/updater/mods/SteamWorkshopMod.java new file mode 100644 index 00000000..5510b49f --- /dev/null +++ b/src/main/java/com/osiris/autoplug/client/tasks/updater/mods/SteamWorkshopMod.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 Osiris-Team. + * All rights reserved. + * + * This software is copyrighted work, licensed under the terms + * of the MIT-License. Consult the "LICENSE" file for details. + */ + +package com.osiris.autoplug.client.tasks.updater.mods; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class SteamWorkshopMod extends MinecraftMod { + private final File directory; + private String publishedId; + + public SteamWorkshopMod(File directory, String name, String publishedId) { + super(directory.getAbsolutePath(), name, publishedId, "Steam Workshop", null, null, null); + this.directory = directory; + this.publishedId = publishedId; + } + + public File getDirectory() { + return directory; + } + + public String getPublishedId() { + return publishedId; + } + + public void setPublishedId(String publishedId) { + this.publishedId = publishedId; + setVersion(publishedId); + } + + @NotNull + public static List findIn(File dir) throws IOException { + if (!dir.exists()) throw new FileNotFoundException("Directory does not exist: " + dir); + List mods = new ArrayList<>(); + File[] files = dir.listFiles(); + if (files == null) return mods; + Arrays.sort(files, Comparator.comparing(File::getName)); + for (File file : files) { + if (!file.isDirectory()) continue; + File metaFile = new File(file, "meta.cpp"); + if (metaFile.exists()) + mods.add(readFromMeta(file, metaFile)); + } + return mods; + } + + static SteamWorkshopMod readFromMeta(File modDir, File metaFile) throws IOException { + String name = modDir.getName(); + String publishedId = null; + for (String line : Files.readAllLines(metaFile.toPath(), StandardCharsets.UTF_8)) { + String trimmedLine = line.trim(); + if (trimmedLine.isEmpty() || trimmedLine.startsWith("//")) continue; + + int equalsIndex = trimmedLine.indexOf('='); + if (equalsIndex < 0) continue; + + String key = trimmedLine.substring(0, equalsIndex).trim(); + String value = trimmedLine.substring(equalsIndex + 1).trim(); + int semicolonIndex = value.indexOf(';'); + if (semicolonIndex < 0) continue; + value = value.substring(0, semicolonIndex).trim(); + if (value.startsWith("\"") && value.endsWith("\"") && value.length() >= 2) + value = value.substring(1, value.length() - 1); + + if (key.equals("name")) name = value; + if (key.equals("publishedid")) publishedId = value; + } + + if (publishedId == null || !publishedId.matches("\\d+")) + throw new IOException("Failed to read publishedid from " + metaFile); + + return new SteamWorkshopMod(modDir, name, publishedId); + } +} diff --git a/src/main/java/com/osiris/autoplug/client/tasks/updater/mods/TaskModsUpdater.java b/src/main/java/com/osiris/autoplug/client/tasks/updater/mods/TaskModsUpdater.java index 6d0b078c..5ec1078f 100644 --- a/src/main/java/com/osiris/autoplug/client/tasks/updater/mods/TaskModsUpdater.java +++ b/src/main/java/com/osiris/autoplug/client/tasks/updater/mods/TaskModsUpdater.java @@ -15,6 +15,7 @@ import com.osiris.autoplug.client.tasks.updater.plugins.ResourceFinder; import com.osiris.autoplug.client.tasks.updater.search.SearchResult; import com.osiris.autoplug.client.utils.GD; +import com.osiris.autoplug.client.utils.SteamCMD; import com.osiris.autoplug.client.utils.UtilsFile; import com.osiris.autoplug.client.utils.UtilsMinecraft; import com.osiris.betterthread.BThread; @@ -22,6 +23,8 @@ import com.osiris.betterthread.BWarning; import com.osiris.dyml.YamlSection; import com.osiris.dyml.exceptions.DuplicateKeyException; +import com.osiris.jlib.logger.AL; +import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.NotNull; import java.io.DataInputStream; @@ -81,7 +84,9 @@ public void runAtStart() throws Exception { setStatus("Fetching latest mod data..."); userProfile = updaterConfig.mods_updater_profile.asString(); - this.allMods.addAll(new UtilsMinecraft().getMods(FileManager.convertRelativeToAbsolutePath(updaterConfig.mods_updater_path.asString()))); + File modsDir = FileManager.convertRelativeToAbsolutePath(updaterConfig.mods_updater_path.asString()); + this.allMods.addAll(new UtilsMinecraft().getMods(modsDir)); + this.allMods.addAll(SteamWorkshopMod.findIn(modsDir)); for (MinecraftMod installedMod : allMods) { @@ -96,6 +101,7 @@ public void runAtStart() throws Exception { YamlSection author = modsConfig.put(name, plName, "author").setDefValues(installedMod.getAuthor()); YamlSection modrinthId = modsConfig.put(name, plName, "modrinth-id"); YamlSection curseforgeId = modsConfig.put(name, plName, "curseforge-id"); + YamlSection steamWorkshopId = modsConfig.put(name, plName, "steam-workshop-id"); YamlSection ignoreContentType = modsConfig.put(name, plName, "ignore-content-type").setDefValues("false"); YamlSection forceLatest = modsConfig.put(name, plName, "force-latest").setDefValues("false"); YamlSection forceUpdate = modsConfig.put(name, plName, "force-update").setDefValues("false"); @@ -111,6 +117,8 @@ public void runAtStart() throws Exception { modrinthId.setValues(installedMod.modrinthId); if (installedMod.curseforgeId != null && curseforgeId.asString() == null) curseforgeId.setValues(installedMod.curseforgeId); + if (installedMod instanceof SteamWorkshopMod) + steamWorkshopId.setValues(((SteamWorkshopMod) installedMod).getPublishedId()); // Update the detailed mods in-memory values installedMod.modrinthId = (modrinthId.asString()); @@ -125,6 +133,14 @@ public void runAtStart() throws Exception { installedMod.jenkinsArtifactName = (jenkinsArtifactName.asString()); installedMod.jenkinsBuildId = (jenkinsBuildId.asInt()); installedMod.forceUpdate = forceUpdate.asBoolean(); + if (installedMod instanceof SteamWorkshopMod) { + ((SteamWorkshopMod) installedMod).setPublishedId(steamWorkshopId.asString()); + if (exclude.asBoolean()) + excludedMods.add(installedMod); + else + includedMods.add(installedMod); + continue; + } // Check for missing author in internal config if ((installedMod.getVersion() == null) @@ -184,11 +200,7 @@ public void runAtStart() throws Exception { int sizeBukkitMods = 0; int sizeUnknownMods = 0; int sizeCustomMods = 0; - - - String mcVersion = updaterConfig.mods_updater_version.asString(); - if (mcVersion == null) updaterConfig.server_updater_version.asString(); - if (mcVersion == null) mcVersion = Server.getMCVersion(); + int sizeSteamWorkshopMods = 0; ExecutorService executorService; if (updaterConfig.mods_updater_async.asBoolean()) @@ -197,11 +209,15 @@ public void runAtStart() throws Exception { executorService = Executors.newSingleThreadExecutor(); InstalledModLoader modLoader = new InstalledModLoader(); List> activeFutures = new ArrayList<>(); + String mcVersion = null; for (MinecraftMod mod : includedMods) { try { setStatus("Initialising update check for " + mod.getName() + "..."); - if (mod.customCheckURL != null) { // Custom Check + if (mod instanceof SteamWorkshopMod) { + sizeSteamWorkshopMods++; + activeFutures.add(executorService.submit(() -> doSteamWorkshopUpdateLogic((SteamWorkshopMod) mod))); + } else if (mod.customCheckURL != null) { // Custom Check sizeCustomMods++; activeFutures.add(executorService.submit(() -> new ResourceFinder().findByCustomCheckURL(mod))); } else if (mod.jenkinsProjectUrl != null) { // JENKINS MOD @@ -213,6 +229,11 @@ public void runAtStart() throws Exception { } else { sizeUnknownMods++; // MODRINTH OR CURSEFORGE MOD mod.ignoreContentType = true; // TODO temporary workaround for xamazon-json content type curseforge/bukkit issue: https://github.com/Osiris-Team/AutoPlug-Client/issues/109 + if (mcVersion == null) { + mcVersion = updaterConfig.mods_updater_version.asString(); + if (mcVersion == null) mcVersion = updaterConfig.server_updater_version.asString(); + if (mcVersion == null) mcVersion = Server.getMCVersion(); + } String finalMcVersion = mcVersion; activeFutures.add(executorService.submit(() -> new ResourceFinder().findByModrinthOrCurseforge(modLoader, mod, finalMcVersion, updaterConfig.mods_update_check_name_for_mod_loader.asBoolean()))); } @@ -245,7 +266,13 @@ public void runAtStart() throws Exception { String resultModrinthId = mod.modrinthId; String resultCurseForgeId = mod.curseforgeId; this.setStatus("Checked '" + mod.getName() + "' mod (" + results.size() + "/" + includedSize + ")"); - if (code == SearchResult.Type.UP_TO_DATE || code == SearchResult.Type.UPDATE_AVAILABLE) { + if (mod instanceof SteamWorkshopMod) { + if (code == SearchResult.Type.API_ERROR) + if (result.getException() != null) + getWarnings().add(new BWarning(this, result.getException(), "There was a Steam Workshop update error for " + mod.getName() + "!")); + else + getWarnings().add(new BWarning(this, new Exception("There was a Steam Workshop update error for " + mod.getName() + "!"))); + } else if (code == SearchResult.Type.UP_TO_DATE || code == SearchResult.Type.UPDATE_AVAILABLE) { doDownloadLogic(mod, result); } else if (code == SearchResult.Type.API_ERROR) if (result.getException() != null) @@ -337,6 +364,58 @@ else if (code == SearchResult.Type.RESOURCE_NOT_FOUND) } + private SearchResult doSteamWorkshopUpdateLogic(@NotNull SteamWorkshopMod mod) { + SearchResult result = new SearchResult(null, SearchResult.Type.UP_TO_DATE, mod.getPublishedId(), null, "steam-workshop", null, null, false); + result.mod = mod; + String workshopAppId = updaterConfig.server_software.asString(); + if (workshopAppId == null || !workshopAppId.matches("\\d+")) { + result.type = SearchResult.Type.API_ERROR; + result.setException(new Exception("Steam Workshop mod '" + mod.getName() + "' was found, but server-updater.software is not a numeric Steam app-id.")); + return result; + } + + if (userProfile.equals(notifyProfile)) { + addInfo("NOTIFY: Steam Workshop mod '" + mod.getName() + "' can be updated with Workshop item " + mod.getPublishedId() + " for app " + workshopAppId + "."); + result.type = SearchResult.Type.UPDATE_AVAILABLE; + return result; + } + + try { + SteamCMD steamCMD = createSteamCMD(); + setStatus("Updating Steam Workshop mod '" + mod.getName() + "'..."); + boolean isSuccess = steamCMD.installOrUpdateWorkshopItem(workshopAppId, mod.getPublishedId(), + line -> { + AL.debug(this.getClass(), "SteamCMD-Out: " + line); + setStatus(line); + }, errLine -> { + AL.debug(this.getClass(), "SteamCMD-Err-Out: " + errLine); + setStatus(errLine); + addWarning(errLine); + }); + if (!isSuccess) + throw new Exception("Failed to update Steam Workshop mod '" + mod.getName() + "' via SteamCMD."); + + File downloadedDir = steamCMD.getWorkshopItemDir(workshopAppId, mod.getPublishedId()); + if (userProfile.equals(manualProfile)) { + addInfo("MANUAL: Downloaded Steam Workshop mod '" + mod.getName() + "' to " + downloadedDir.getAbsolutePath() + "."); + result.type = SearchResult.Type.UPDATE_DOWNLOADED; + } else { + setStatus("Copying Steam Workshop mod '" + mod.getName() + "' into " + mod.getDirectory().getAbsolutePath() + "..."); + FileUtils.copyDirectory(downloadedDir, mod.getDirectory()); + addInfo("Updated Steam Workshop mod '" + mod.getName() + "' from Workshop item " + mod.getPublishedId() + "."); + result.type = SearchResult.Type.UPDATE_INSTALLED; + } + } catch (Exception e) { + result.type = SearchResult.Type.API_ERROR; + result.setException(e); + } + return result; + } + + SteamCMD createSteamCMD() { + return new SteamCMD(); + } + private void doDownloadLogic(@NotNull MinecraftMod mod, SearchResult result) { SearchResult.Type code = result.type; String type = result.getDownloadType(); // The file type to download (Note: When 'external' is returned nothing will be downloaded. Working on a fix for this!) diff --git a/src/main/java/com/osiris/autoplug/client/utils/SteamCMD.java b/src/main/java/com/osiris/autoplug/client/utils/SteamCMD.java index 9c971e52..6b0f8dfa 100644 --- a/src/main/java/com/osiris/autoplug/client/utils/SteamCMD.java +++ b/src/main/java/com/osiris/autoplug/client/utils/SteamCMD.java @@ -35,6 +35,7 @@ @SuppressWarnings({"WeakerAccess", "unused"}) public class SteamCMD { + private static final String STEAMCMD_WORKSHOP_COMMAND = "+login {LOGIN} +workshop_download_item {WORKSHOP_APP} {WORKSHOP_ITEM} validate +quit"; private final String steamcmdArchive = "steamcmd" + (isWindows ? ".zip" : isMac ? "_osx.tar.gz" : "_linux.tar.gz"); private final String steamcmdExtension = isWindows ? ".exe" : ".sh"; private final String steamcmdExecutable = "steamcmd" + steamcmdExtension; @@ -115,8 +116,7 @@ public boolean installOrUpdateServer(String appId, Consumer onLog, Consu AL.debug(this.getClass(), "Installing app " + appId + "..."); onLog.accept("Installing app " + appId + "..."); - String login = new UpdaterConfig().server_steamcmd_login.asString(); - if (login == null || login.isEmpty()) login = "anonymous"; + String login = getLogin(); File gameInstallDir = new File(dirSteamServersDownloads + "/" + appId); gameInstallDir.mkdirs(); @@ -159,10 +159,65 @@ public boolean installOrUpdateServer(String appId, Consumer onLog, Consu } } + public boolean installOrUpdateWorkshopItem(String workshopAppId, String workshopItemId, Consumer onLog, Consumer onLogErr) { + try { + if (!installIfNeeded()) return false; + AL.debug(this.getClass(), "Installing workshop item " + workshopItemId + " for app " + workshopAppId + "..."); + onLog.accept("Installing workshop item " + workshopItemId + "..."); + + String command = buildWorkshopItemCommand(getLogin(), workshopAppId, workshopItemId); + List logLines = new ArrayList<>(); + List logErrLines = new ArrayList<>(); + AtomicBoolean isFinished = new AtomicBoolean(false); + AtomicBoolean isSuccess = new AtomicBoolean(true); + AsyncTerminal terminal = new AsyncTerminal(destDir, line -> { + onLog.accept(line); + logLines.add(line); + String lowerLine = line.toLowerCase(); + if (lowerLine.startsWith("success.") && lowerLine.contains("item " + workshopItemId.toLowerCase())) + isFinished.set(true); + if (lowerLine.startsWith("error!")) { + isSuccess.set(false); + isFinished.set(true); + } + }, line -> { + onLogErr.accept(line); + logErrLines.add(line); + }, destExe.getAbsolutePath() + " " + command); + + Thread thread = new Thread(() -> terminal.process.destroy()); + Runtime.getRuntime().addShutdownHook(thread); + while (!isFinished.get() && terminal.process.isAlive()) Thread.sleep(100); + if (terminal.process.isAlive()) terminal.process.destroy(); + Runtime.getRuntime().removeShutdownHook(thread); + return isSuccess.get() && getWorkshopItemDir(workshopAppId, workshopItemId).exists(); + } catch (Exception e) { + AL.warn(e); + return false; + } + } + + public File getWorkshopItemDir(String workshopAppId, String workshopItemId) { + return new File(destDir + "/steamapps/workshop/content/" + workshopAppId + "/" + workshopItemId); + } + + static String buildWorkshopItemCommand(String login, String workshopAppId, String workshopItemId) { + return STEAMCMD_WORKSHOP_COMMAND + .replace("{LOGIN}", login) + .replace("{WORKSHOP_APP}", workshopAppId) + .replace("{WORKSHOP_ITEM}", workshopItemId); + } + + private String getLogin() throws NotLoadedException, YamlReaderException, YamlWriterException, IOException, IllegalKeyException, DuplicateKeyException, IllegalListException { + String login = new UpdaterConfig().server_steamcmd_login.asString(); + if (login == null || login.isEmpty()) login = "anonymous"; + return login; + } + public String getResolutionForError(String error) { for (Map.Entry entry : errorResolutions.entrySet()) if (error.contains(entry.getKey())) return entry.getValue(); return "Unknown. :("; } -} \ No newline at end of file +} diff --git a/src/test/java/com/osiris/autoplug/client/tasks/updater/mods/SteamWorkshopModTest.java b/src/test/java/com/osiris/autoplug/client/tasks/updater/mods/SteamWorkshopModTest.java new file mode 100644 index 00000000..6a651220 --- /dev/null +++ b/src/test/java/com/osiris/autoplug/client/tasks/updater/mods/SteamWorkshopModTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2024 Osiris-Team. + * All rights reserved. + * + * This software is copyrighted work, licensed under the terms + * of the MIT-License. Consult the "LICENSE" file for details. + */ + +package com.osiris.autoplug.client.tasks.updater.mods; + +import com.osiris.autoplug.client.configs.ModsConfig; +import com.osiris.autoplug.client.configs.UpdaterConfig; +import com.osiris.autoplug.client.utils.GD; +import com.osiris.autoplug.client.utils.SteamCMD; +import com.osiris.betterthread.BThreadManager; +import com.osiris.jlib.logger.AL; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class SteamWorkshopModTest { + + @TempDir + Path tempDir; + + @Test + void readsPublishedIdAndNameFromMetaCpp() throws Exception { + File modDir = tempDir.resolve("@CF").toFile(); + modDir.mkdirs(); + File metaFile = writeMeta(modDir, + "protocol = 1;", + "publishedid = 1559212036;", + "name = \"CF\";", + "timestamp = 5249804932187309401;"); + + SteamWorkshopMod mod = SteamWorkshopMod.readFromMeta(modDir, metaFile); + + assertEquals(modDir, mod.getDirectory()); + assertEquals("CF", mod.getName()); + assertEquals("1559212036", mod.getPublishedId()); + } + + @Test + void findsModsWithMetaCppInDirectSubdirectories() throws Exception { + File firstModDir = tempDir.resolve("@CF").toFile(); + File secondModDir = tempDir.resolve("@CommunityOnlineTools").toFile(); + File ignoredDir = tempDir.resolve("keys").toFile(); + firstModDir.mkdirs(); + secondModDir.mkdirs(); + ignoredDir.mkdirs(); + writeMeta(firstModDir, "publishedid = 1559212036;", "name = \"CF\";"); + writeMeta(secondModDir, "publishedid = 1564026768;", "name = \"Community-Online-Tools\";"); + + List mods = SteamWorkshopMod.findIn(tempDir.toFile()); + + assertEquals(2, mods.size()); + assertEquals("1559212036", mods.get(0).getPublishedId()); + assertEquals("1564026768", mods.get(1).getPublishedId()); + } + + @Test + void taskUpdatesWorkshopModThroughSteamCmdAndCachesMetadata() throws Exception { + File oldWorkingDir = GD.WORKING_DIR; + File oldDownloadsDir = GD.DOWNLOADS_DIR; + String oldUserDir = System.getProperty("user.dir"); + useWorkingDir(tempDir); + try { + File modDir = tempDir.resolve("mods/@CF").toFile(); + modDir.mkdirs(); + writeMeta(modDir, "publishedid = 1559212036;", "name = \"CF\";"); + Files.write(modDir.toPath().resolve("old.txt"), Arrays.asList("old"), StandardCharsets.UTF_8); + + File downloadedDir = tempDir.resolve("steamcmd/steamapps/workshop/content/221100/1559212036").toFile(); + downloadedDir.mkdirs(); + Files.write(downloadedDir.toPath().resolve("updated.txt"), Arrays.asList("updated"), StandardCharsets.UTF_8); + + UpdaterConfig updaterConfig = new UpdaterConfig(); + updaterConfig.mods_updater.setValues("true"); + updaterConfig.mods_updater_profile.setValues("AUTOMATIC"); + updaterConfig.mods_updater_path.setValues("./mods"); + updaterConfig.mods_updater_async.setValues("false"); + updaterConfig.server_software.setValues("221100"); + updaterConfig.save(); + + FakeSteamCMD steamCMD = new FakeSteamCMD(downloadedDir); + TaskModsUpdater task = new TaskModsUpdater("ModsUpdater", new BThreadManager()) { + @Override + SteamCMD createSteamCMD() { + return steamCMD; + } + }; + + task.runAtStart(); + + assertEquals(1, steamCMD.updateCalls); + assertEquals("221100", steamCMD.workshopAppId); + assertEquals("1559212036", steamCMD.workshopItemId); + assertEquals("updated", Files.readAllLines(modDir.toPath().resolve("updated.txt"), StandardCharsets.UTF_8).get(0)); + assertTrue(task.getWarnings().isEmpty()); + + ModsConfig modsConfig = new ModsConfig(); + modsConfig.load(); + assertEquals("1559212036", modsConfig.get("mods", "CF", "steam-workshop-id").asString()); + } finally { + System.setProperty("user.dir", oldUserDir); + GD.WORKING_DIR = oldWorkingDir; + GD.DOWNLOADS_DIR = oldDownloadsDir; + } + } + + private File writeMeta(File modDir, String... lines) throws Exception { + File metaFile = new File(modDir, "meta.cpp"); + Files.write(metaFile.toPath(), Arrays.asList(lines), StandardCharsets.UTF_8); + return metaFile; + } + + private void useWorkingDir(Path dir) throws IOException { + System.setProperty("user.dir", dir.toAbsolutePath().toString()); + GD.VERSION = "AutoPlug-Client Test-Version"; + GD.WORKING_DIR = dir.toFile(); + GD.DOWNLOADS_DIR = dir.resolve("autoplug/downloads").toFile(); + GD.DOWNLOADS_DIR.mkdirs(); + File logFile = dir.resolve("autoplug/logs/latest.log").toFile(); + logFile.getParentFile().mkdirs(); + new AL().start("AL", true, logFile, false, false); + } + + private static class FakeSteamCMD extends SteamCMD { + final File workshopItemDir; + int updateCalls; + String workshopAppId; + String workshopItemId; + + FakeSteamCMD(File workshopItemDir) { + this.workshopItemDir = workshopItemDir; + } + + @Override + public boolean installOrUpdateWorkshopItem(String workshopAppId, String workshopItemId, Consumer onLog, Consumer onLogErr) { + this.updateCalls++; + this.workshopAppId = workshopAppId; + this.workshopItemId = workshopItemId; + onLog.accept("Success. Downloaded item " + workshopItemId); + return true; + } + + @Override + public File getWorkshopItemDir(String workshopAppId, String workshopItemId) { + return workshopItemDir; + } + } +} diff --git a/src/test/java/com/osiris/autoplug/client/utils/SteamCMDTest.java b/src/test/java/com/osiris/autoplug/client/utils/SteamCMDTest.java index 7d39cf1f..95c1ca44 100644 --- a/src/test/java/com/osiris/autoplug/client/utils/SteamCMDTest.java +++ b/src/test/java/com/osiris/autoplug/client/utils/SteamCMDTest.java @@ -13,11 +13,20 @@ import java.io.IOException; +import static org.junit.jupiter.api.Assertions.assertEquals; + class SteamCMDTest { + @Test + void buildsWorkshopItemCommand() { + String command = SteamCMD.buildWorkshopItemCommand("anonymous", "221100", "1559212036"); + + assertEquals("+login anonymous +workshop_download_item 221100 1559212036 validate +quit", command); + } + @Test void installSteamcmd() throws IOException { UtilsTest.init(); new SteamCMD().installIfNeeded(); } -} \ No newline at end of file +}