-
Notifications
You must be signed in to change notification settings - Fork 27
Add DayZ workshop mod updates #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<SteamWorkshopMod> findIn(File dir) throws IOException { | ||
| if (!dir.exists()) throw new FileNotFoundException("Directory does not exist: " + dir); | ||
| List<SteamWorkshopMod> 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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,13 +15,16 @@ | |
| 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; | ||
| import com.osiris.betterthread.BThreadManager; | ||
| 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<Future<SearchResult>> 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))); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if I understood correctly, this would mean we always update the mod, no matter if it actually has an update available, which is not optimal. please take a look at ResourceFinder and ensure there is some similar way to check the latest version via steamCMD and if it has an update. |
||
| } 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) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. integrate the custom code into the existing doDownloadLogic function too please. and preferably use something similar to TaskModDownload than runs steamcmd internally for example. |
||
| 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!) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is no need to move this block into the loop below