package at.hannibal2.skyhanni.repo import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.RepositoryReloadEvent import at.hannibal2.skyhanni.utils.LorenzUtils import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.JsonObject import net.minecraft.client.Minecraft import org.apache.commons.io.FileUtils import java.io.* import java.net.URL import java.nio.charset.StandardCharsets import java.util.concurrent.CompletableFuture import java.util.concurrent.atomic.AtomicBoolean class RepoManager(private val configLocation: File) { val gson: Gson = GsonBuilder().setPrettyPrinting().create() private var latestRepoCommit: String? = null private val repoLocation: File = File(configLocation, "repo") fun loadRepoInformation() { if (SkyHanniMod.feature.apiData.repoAutoUpdate) { fetchRepository().thenRun(this::reloadRepository) } else { reloadRepository() } } private val atomicShouldManuallyReload = AtomicBoolean(false)//TODO FIX fun updateRepo() { atomicShouldManuallyReload.set(true) fetchRepository(true).thenRun { this.reloadRepository("Repo updated successful :)") } } fun reloadLocalRepo() { atomicShouldManuallyReload.set(true) reloadRepository("Repo loaded from local files successful :)") } private fun fetchRepository(command: Boolean = false): CompletableFuture { return CompletableFuture.supplyAsync { try { val currentCommitJSON: JsonObject? = getJsonFromFile(File(configLocation, "currentCommit.json")) latestRepoCommit = null try { InputStreamReader(URL(getCommitApiUrl()).openStream()) .use { inReader -> val commits: JsonObject = gson.fromJson(inReader, JsonObject::class.java) latestRepoCommit = commits["sha"].asString } } catch (e: Exception) { e.printStackTrace() } if (latestRepoCommit == null || latestRepoCommit!!.isEmpty()) return@supplyAsync false if (File(configLocation, "repo").exists()) { if (currentCommitJSON != null && currentCommitJSON["sha"].asString == latestRepoCommit) { if (command) { LorenzUtils.chat("§e[SkyHanni] §7The repo is already up to date!") atomicShouldManuallyReload.set(false) } return@supplyAsync false } } RepoUtils.recursiveDelete(repoLocation) repoLocation.mkdirs() val itemsZip = File(repoLocation, "sh-repo-main.zip") try { itemsZip.createNewFile() } catch (e: IOException) { return@supplyAsync false } val url = URL(getDownloadUrl(latestRepoCommit)) val urlConnection = url.openConnection() urlConnection.connectTimeout = 15000 urlConnection.readTimeout = 30000 try { urlConnection.getInputStream().use { `is` -> FileUtils.copyInputStreamToFile( `is`, itemsZip ) } } catch (e: IOException) { e.printStackTrace() System.err.println("Failed to download SkyHanni Repo! Please report this issue to the mod creator") if (command) { LorenzUtils.error("An error occurred while trying to reload the repo! See logs for more info.") } return@supplyAsync false } RepoUtils.unzipIgnoreFirstFolder( itemsZip.absolutePath, repoLocation.absolutePath ) if (currentCommitJSON == null || currentCommitJSON["sha"].asString != latestRepoCommit) { val newCurrentCommitJSON = JsonObject() newCurrentCommitJSON.addProperty("sha", latestRepoCommit) try { writeJson(newCurrentCommitJSON, File(configLocation, "currentCommit.json")) } catch (ignored: IOException) { } } } catch (e: Exception) { e.printStackTrace() } true } } private fun reloadRepository(answerMessage: String = ""): CompletableFuture { val comp = CompletableFuture() if (!atomicShouldManuallyReload.get()) return comp Minecraft.getMinecraft().addScheduledTask { try { RepositoryReloadEvent(repoLocation, gson).postAndCatch() comp.complete(null) if (answerMessage.isNotEmpty()) { LorenzUtils.chat("§e[SkyHanni] §a$answerMessage") } } catch (e: java.lang.Exception) { comp.completeExceptionally(e) LorenzUtils.error("An error occurred while trying to reload the repo! See logs for more info.") } } return comp } /** * Parses a file in to a JsonObject. */ private fun getJsonFromFile(file: File?): JsonObject? { try { BufferedReader( InputStreamReader( FileInputStream(file), StandardCharsets.UTF_8 ) ).use { reader -> return gson.fromJson(reader, JsonObject::class.java) } } catch (e: java.lang.Exception) { return null } } private fun getCommitApiUrl(): String { val repoUser = "hannibal00212" val repoName = "SkyHanni-REPO" val repoBranch = "main" return String.format("https://api.github.com/repos/%s/%s/commits/%s", repoUser, repoName, repoBranch) } private fun getDownloadUrl(commitId: String?): String { val repoUser = "hannibal00212" val repoName = "SkyHanni-REPO" return String.format("https://github.com/%s/%s/archive/%s.zip", repoUser, repoName, commitId) } @Throws(IOException::class) fun writeJson(json: JsonObject?, file: File) { file.createNewFile() BufferedWriter( OutputStreamWriter( FileOutputStream(file), StandardCharsets.UTF_8 ) ).use { writer -> writer.write(gson.toJson(json)) } } }