diff --git a/.gitignore b/.gitignore index 16bdd9507..d69b9c48c 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ build.properties # Sqlite databases *.sqlite + +# Don't add access transformers to git as it's dynamically generated +accesstransformer.cfg diff --git a/build.gradle b/build.gradle index f6a938675..e6c680bd0 100644 --- a/build.gradle +++ b/build.gradle @@ -8,13 +8,18 @@ plugins { id "io.github.pacifistmc.forgix" version "1.2.6" // Manifold preprocessor - id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha" +// id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha" // // Provides mc libraries to core // id "org.spongepowered.gradle.vanilla" version '0.2.1-SNAPSHOT' apply false // Architectury is used here only as a replacement for forge's own loom - id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false +// id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false + id "fabric-loom" version "1.4-SNAPSHOT" apply false + + id 'net.minecraftforge.gradle' version '[6.0.16,6.2)' apply false + id 'org.spongepowered.mixin' version '0.7.+' apply false + id 'org.parchmentmc.librarian.forgegradle' version '1.+' apply false } /** @@ -104,8 +109,8 @@ subprojects { p -> // Apply plugins apply plugin: "java" apply plugin: "com.github.johnrengelman.shadow" - if (isMinecraftSubProject) - apply plugin: "systems.manifold.manifold-gradle-plugin" +// if (isMinecraftSubProject) +// apply plugin: "systems.manifold.manifold-gradle-plugin" if (p == project(":core")) apply plugin: "application" // apply plugin: "org.spongepowered.gradle.vanilla" // Provides minecraft libraries @@ -114,14 +119,20 @@ subprojects { p -> if ( (findProject(":forge") && p == project(":forge")) || (findProject(":neoforge") && p == project(":neoforge")) - ) - apply plugin: "dev.architectury.loom" + ) { +// apply plugin: 'net.minecraftforge.gradle' +// apply plugin: 'org.spongepowered.mixin' +// apply plugin: 'org.parchmentmc.librarian.forgegradle' + + /* The code below creates the access transformer file */ + new AWToAT().remap(project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener"), accessWidenerVersion) + } // Set the manifold version (may not be required tough) - manifold { - manifoldVersion = rootProject.manifold_version - } +// manifold { +// manifoldVersion = rootProject.manifold_version +// } // set up custom configurations (configurations are a way to handle dependencies) @@ -143,7 +154,7 @@ subprojects { p -> // this should match shadowMe pretty closely implementation.extendsFrom(forgeShadowMe) shadowMe.extendsFrom(forgeShadowMe) - forgeRuntimeLibrary.extendsFrom(forgeShadowMe) + runtimeOnly.extendsFrom(forgeShadowMe) if (isMinecraftSubProject && p != project(":common")) { @@ -154,14 +165,18 @@ subprojects { p -> runtimeClasspath.extendsFrom common if (findProject(":forge")) developmentForge.extendsFrom common + implementation.extendsFrom common if (findProject(":neoforge")) developmentNeoForge.extendsFrom common + implementation.extendsFrom common compileClasspath.extendsFrom coreProjects runtimeClasspath.extendsFrom coreProjects if (findProject(":forge")) developmentForge.extendsFrom coreProjects + implementation.extendsFrom coreProjects if (findProject(":neoforge")) developmentNeoForge.extendsFrom coreProjects + implementation.extendsFrom coreProjects if (findProject(":fabricLike") && p != project(":fabricLike")) { // Shadow fabricLike @@ -615,7 +630,7 @@ allprojects { p -> into p.file("build/resources/main") } - tasks.withType(JavaCompile) { + compileJava { if (isMinecraftSubProject) { options.release = rootProject.java_version as Integer options.compilerArgs += ["-Xplugin:Manifold"] diff --git a/buildSrc/src/main/java/AWToAT.java b/buildSrc/src/main/java/AWToAT.java new file mode 100644 index 000000000..2f851b264 --- /dev/null +++ b/buildSrc/src/main/java/AWToAT.java @@ -0,0 +1,239 @@ +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AWToAT { + private static final Map ACCESS_POINT_MAP = new HashMap<>(); + + static { + ACCESS_POINT_MAP.put("accessible", "public"); + ACCESS_POINT_MAP.put("extendable", "public-f"); + ACCESS_POINT_MAP.put("mutable", "public-f"); + } + + public String minecraftVersion; + + public File remap(File file, String minecraftVersion) { + this.minecraftVersion = minecraftVersion.replace("_", "."); + File atFile = createATFile(file); + processFile(file, atFile); + return atFile; + } + + private File createATFile(File file) { + File metaInf = new File(file.getParentFile(), "META-INF"); + if (!metaInf.exists() && !metaInf.mkdir()) throw new RuntimeException("Error creating META-INF folder"); + File atFile = new File(metaInf, "accesstransformer.cfg"); + try { + atFile.createNewFile(); + } catch (IOException e) { + throw new RuntimeException("Error creating new file", e); + } + return atFile; + } + + private void processFile(File file, File atFile) { + /* Validates if we need to recreate the Access Transformer file if it's out of date */ + // Get the hash of the file + String fileHash = getFileHash(file); + try (Scanner atScanner = new Scanner(atFile)) { + // Check if the AT file is up-to-date by comparing the hash of the file with the hash stored in the AT file + boolean hashFound = false; + while (atScanner.hasNextLine()) { + String line = atScanner.nextLine(); + if (hashCheck(line, fileHash)) { + hashFound = true; + } + } + + // If the AT file is up-to-date, print a message and return + if (hashFound) { + System.out.println("Access Transformer file is already up to date."); + return; + } + } catch (FileNotFoundException ignored) { + // If the AT file does not exist, continue + } + + /* Creates the Access Transformer file */ + // Opens a scanner for reading the Access Widener file and a writer for writing to the Access Transformer file + try (Scanner scanner = new Scanner(file); FileWriter writer = new FileWriter(atFile)) { + // Create an ExecutorService with a fixed thread pool size equal to the number of available processors + ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + // List to hold Future objects representing results of computation + List> futures = new ArrayList<>(); + + // Write the hash of the file to the AT file + writer.write("#DH_MAPPING_HASH:" + fileHash + "\n"); + + // Read each line from the file + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + // Skip lines starting with "accessWidener", "#" or blank lines + if (line.startsWith("accessWidener") || line.startsWith("#") || line.isBlank()) continue; + + // Submit the line to the executor service for processing + // The processing is done by the processLine method + futures.add(executor.submit(() -> processLine(line.split(" ")))); + } + + // Write the results to the output file + // The results are obtained by calling the get method on each Future + for (Future future : futures) { + writer.write(future.get()); + } + + // Shutdown the executor service to free up resources + executor.shutdown(); + } catch (Exception e) { + throw new RuntimeException("Error reading or writing to file", e); + } + } + + private String processLine(String[] fields) { + // fields[0] = access point like "accessible", "extendable", "mutable" + // fields[1] = type like "field", "method", "class" + // fields[2] = class name + // fields[3] = field/method name + // fields[4] = field/method descriptor + + try { + // Store the original field/method name + String originalName = ""; + + // If there is a class name, replace the slashes with dots in the package name + if (fields.length > 2) fields[2] = fields[2].replace("/", "."); + + // If there is a field/method name, store the original name and remap it to SRG + if (fields.length > 3) { + originalName = fields[3]; + fields[3] = remapToSRG(fields[2], fields[3]); + } + + StringBuilder line = new StringBuilder(ACCESS_POINT_MAP.getOrDefault(fields[0], "public")).append(" "); + switch (fields[1]) { + case "field": + line.append(fields[2]).append(" ").append(fields[3]).append(" #").append(originalName); + // It'll be like: access-point class-name field-name-SRG # field-name-Mojmap + // Eg: public net.minecraft.client.Minecraft f_90981_ # instance + break; + case "method": + line.append(fields[2]).append(" ").append(fields[3]).append(fields[4]).append(" #").append(originalName); + // It'll be like: access-point class-name method-name-SRG method-descriptor # method-name-Mojmap + // Eg: public net.minecraft.client.Minecraft m_172797_()Lnet/minecraft/client/Minecraft; # getInstance + break; + default: + line.append(fields[2]); + // It'll be like: access-point class-name + // Eg: public net.minecraft.client.Minecraft + break; + } + line.append("\n"); + return line.toString(); + } catch (Exception e) { + throw new RuntimeException("Error processing line", e); + } + } + + private boolean hashCheck(String line, String fileHash) { + if (line.startsWith("#DH_MAPPING_HASH:")) { + String hash = line.substring(17); + return hash.equals(fileHash); + } + return false; + } + + public String getFileHash(File file) { + try { + MessageDigest shaDigest = MessageDigest.getInstance("SHA-256"); + try (InputStream fis = new FileInputStream(file)) { + byte[] byteArray = new byte[1024]; + int bytesCount; + + // Read file data and update in message digest + while ((bytesCount = fis.read(byteArray)) != -1) { + shaDigest.update(byteArray, 0, bytesCount); + } + } + + byte[] bytes = shaDigest.digest(); + + // Convert byte array into signum representation + StringBuilder sb = new StringBuilder(); + for (byte aByte : bytes) { + sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1)); + } + + // Return complete hash + return sb.toString(); + } catch (NoSuchAlgorithmException | IOException e) { + throw new RuntimeException(e); + } + } + + + + // WARNING: BELOW LIES HIGHLY CURSED CODE AND MIGHT EVEN BE ILLEGAL + + + + + // Flag to track if there was an error in the GET request + boolean error = false; + + /** + * This method returns a field or method name from Mojang mappings as SRG mappings. + * It makes a GET request to the Linkie API to fetch the SRG name. + * + * @param clazz The class name + * @param name The field or method name + * @return The SRG name + * @throws Exception If there is an error in the GET request or the SRG name is not found in the response + */ + private String remapToSRG(String clazz, String name) throws Exception { + // Encode the class and field/method name to be used in the URL + String query = URLEncoder.encode(clazz + "." + name, StandardCharsets.UTF_8); + // Construct the URL for the GET request + String urlString = "https://linkieapi.shedaniel.me/api/search?namespace=mojang&query=" + query + "&version=" + this.minecraftVersion + "&limit=1&allowClasses=false&allowFields=true&allowMethods=true&translate=mojang_srg"; + URL url = new URI(urlString).toURL(); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + int responseCode = conn.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String inputLine; + StringBuilder content = new StringBuilder(); + // Read the response line by line + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + in.close(); + conn.disconnect(); + // Regex to find the SRG name in the response + Pattern pattern = Pattern.compile("\"l\"\\s*:\\s*\\{[^}]*\"i\"\\s*:\\s*\"([^\"]*)\""); + Matcher matcher = pattern.matcher(content.toString()); + if (matcher.find()) return matcher.group(1); + else throw new Exception("Couldn't find the SRG mapping for name: " + name + "\nCould not find 'i' in 'l' object in the response"); // `i` is the SRG name which is stored in the `l` JSON object + } else { + if (error) { + // If there was an error in the GET request, and we already tried again, throw an exception + throw new Exception("The GET request failed"); + } + // If there was an error in the GET request, wait 2.5 seconds and try again as we probably got rate limited + error = true; + Thread.sleep(2500); + return remapToSRG(clazz, name); + } + } +} \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle index ee52d6df3..16b860c36 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,6 +1,4 @@ -plugins { - id "fabric-loom" version "1.4-SNAPSHOT" -} +apply plugin: "fabric-loom" loom { accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") @@ -22,17 +20,12 @@ loom { } } -remapJar { - inputFile = shadowJar.archiveFile - dependsOn shadowJar -// classifier null -} - configurations { // The addModJar basically embeds the mod to the built jar addModJar include.extendsFrom addModJar modImplementation.extendsFrom addModJar + dummy } def addMod(path, enabled) { @@ -114,6 +107,12 @@ dependencies { } } +remapJar { + inputFile = shadowJar.archiveFile + dependsOn shadowJar +// classifier null +} + task deleteResources(type: Delete) { delete file("build/resources/main") diff --git a/forge/build.gradle b/forge/build.gradle index 9c159a519..5b39fd074 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -1,15 +1,12 @@ -plugins { - // Note: This is only needed for multi-loader projects - // The main architectury loom version is set at the start of the root build.gradle - id "architectury-plugin" version "3.4-SNAPSHOT" -} +apply plugin: 'net.minecraftforge.gradle' +apply plugin: 'org.spongepowered.mixin' -sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17 +//sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17 -architectury { - platformSetupLoomIde() - forge() -} +//architectury { +// platformSetupLoomIde() +// forge() +//} //loom { // forge { @@ -19,61 +16,138 @@ architectury { // } //} -loom { - silentMojangMappingsLicense() // Shut the licencing warning - accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") +//loom { +// silentMojangMappingsLicense() // Shut the licencing warning +// accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") +// +// forge { +// convertAccessWideners = true +// extraAccessWideners.add loom.accessWidenerPath.get().asFile.name +// +// mixinConfigs = [ +// "DistantHorizons.forge.mixins.json" +// ] +// } +// +// // "runs" isn't required, but when we do need it then it can be useful +// runs { +// client { +// client() +// setConfigName("Forge Client") +// ideConfigGenerated(true) +// runDir("../run") +//// vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg) +// } +// server { +// server() +// setConfigName("Forge Server") +// ideConfigGenerated(true) +// runDir("../run") +// } +// } +//} - forge { - convertAccessWideners = true - extraAccessWideners.add loom.accessWidenerPath.get().asFile.name - - mixinConfigs = [ - "DistantHorizons.forge.mixins.json" - ] - } - - // "runs" isn't required, but when we do need it then it can be useful +minecraft { + mappings channel: 'official', version: minecraft_version + accessTransformer = project(":common").file('src/main/resources/META-INF/accesstransformer.cfg') runs { client { - client() - setConfigName("Forge Client") - ideConfigGenerated(true) - runDir("../run") -// vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg) + workingDirectory project.file('run') + ideaModule "${rootProject.name}.${project.name}.main" + taskName 'Client' + args "-mixins.config=${mod_name}.forge.mixins.json" + mods { + modClientRun { + source sourceSets.main + source project(":common").sourceSets.main + source project(":api").sourceSets.main + source project(":core").sourceSets.main + } + } + property 'forge.enabledGameTestNamespaces', mod_id + property 'forge.logging.console.level', 'debug' + properties 'mixin.env.remapRefMap': 'true' + property 'mixin.env.refMapRemappingFile', "${project.projectDir}/build/createSrgToMcp/output.srg" } + server { - server() - setConfigName("Forge Server") - ideConfigGenerated(true) - runDir("../run") + workingDirectory project.file('run') + ideaModule "${rootProject.name}.${project.name}.main" + taskName 'Server' + args "-mixins.config=${mod_name}.forge.mixins.json" + mods { + modServerRun { + source sourceSets.main + source project(":common").sourceSets.main + source project(":api").sourceSets.main + source project(":core").sourceSets.main + } + } + property 'forge.logging.console.level', 'debug' + properties 'mixin.env.remapRefMap': 'true' + property 'mixin.env.refMapRemappingFile', "${project.projectDir}/build/createSrgToMcp/output.srg" + } + + data { + workingDirectory project.file('run') + ideaModule "${rootProject.name}.${project.name}.main" + args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + taskName 'Data' + args "-mixins.config=${mod_name}.forge.mixins.json" + mods { + modDataRun { + source sourceSets.main + source project(":common").sourceSets.main + source project(":api").sourceSets.main + source project(":core").sourceSets.main + } + } + property 'forge.logging.console.level', 'debug' } } } +sourceSets.main.resources.srcDir 'src/generated/resources' -remapJar { - inputFile = shadowJar.archiveFile - dependsOn shadowJar -// classifier null +//remapJar { +// inputFile = shadowJar.archiveFile +// dependsOn shadowJar +//// classifier null +//} +shadowJar { + finalizedBy 'reobfShadowJar' +} +jar.dependsOn('shadowJar') +reobf { + shadowJar {} } +minecraft.runs.all { + lazyToken('minecraft_classpath') { +// configurations.implementation.exclude group: 'org.jetbrains', module: 'annotations' +// configurations.implementation.copyRecursive().resolve().collect { it.absolutePath }.join(File.pathSeparator) +// configurations.runtimeLibrary.copyRecursive().resolve().collect { it.absolutePath }.join(File.pathSeparator) + } +} def addMod(path, enabled) { if (enabled == "2") - dependencies { implementation(path) } +// dependencies { implementation(path) } + dependencies { implementation(fg.deobf(path)) } else if (enabled == "1") - dependencies { modCompileOnly(path) } +// dependencies { modCompileOnly(path) } + dependencies { compileOnly(fg.deobf(path)) } } dependencies { - minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" - mappings loom.layered() { - // Mojmap mappings - officialMojangMappings() - // Parchment mappings (it adds parameter mappings & javadoc) - parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") - } +// minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" +// mappings loom.layered() { +// // Mojmap mappings +// officialMojangMappings() +// // Parchment mappings (it adds parameter mappings & javadoc) +// parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") +// } // Forge - forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}" + minecraft "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}" // Architectury API // if (minecraft_version == "1.16.5") { @@ -89,12 +163,18 @@ dependencies { addMod("curse.maven:TerraForged-363820:${rootProject.terraforged_version}", rootProject.enable_terraforged) addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) + + annotationProcessor "org.spongepowered:mixin:0.8.5:processor" // if (System.getProperty("idea.sync.active") != "true") { // annotationProcessor "org.spongepowered:mixin:0.8.4:processor" // } } +mixin { + add sourceSets.main, "${mod_name}-forge-refmap.json" +} + task deleteResources(type: Delete) { delete file("build/resources/main") } @@ -108,10 +188,14 @@ processResources { dependsOn(tasks.named('copyAllResources')) } -tasks.named('runClient') { - dependsOn(tasks.named('copyAllResources')) - finalizedBy(deleteResources) -} +//processResources { +// dependsOn(tasks.named('copyAllResources')) +//} + +//tasks.named('prepareClient') { +// dependsOn(tasks.named('copyAllResources')) +// finalizedBy(deleteResources) +//} sourcesJar { diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java b/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java index 4195b2dea..a86b24a9d 100644 --- a/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java +++ b/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java @@ -86,7 +86,7 @@ import java.util.List; * @author James Seibel * @version 8-15-2022 */ -@Mod(ModInfo.ID) +@Mod("distanthorizons") // TODO: Change it back to ModInfo.ID when forge works public class ForgeMain implements LodForgeMethodCaller { private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); diff --git a/forge/src/main/resources/DistantHorizons.forge.mixins.json b/forge/src/main/resources/DistantHorizons.forge.mixins.json index b30ef91ed..cd96e8fec 100644 --- a/forge/src/main/resources/DistantHorizons.forge.mixins.json +++ b/forge/src/main/resources/DistantHorizons.forge.mixins.json @@ -20,5 +20,6 @@ "client.MixinTextureUtil" ], "server": [], - "plugin": "com.seibel.distanthorizons.forge.mixins.ForgeMixinPlugin" + "plugin": "com.seibel.distanthorizons.forge.mixins.ForgeMixinPlugin", + "refmap": "DistantHorizons-forge-refmap.json" } diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml index 3f199093b..d9aab7f79 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -29,5 +29,5 @@ issueTrackerURL = "${issues}" mandatory = true # Forge syntax type = "required" # Neoforge syntax versionRange = "${compatible_forgemc_versions}" # Where we set what version of mc it is avalible for - ordering = "NONE" + ordering = "AFTER" side = "BOTH" \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ac5579831..68434b1e9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,9 +2,11 @@ org.gradle.jvmargs=-Xmx4096M org.gradle.parallel=true org.gradle.caching=true +fabric.loom.multiProjectOptimisation=true # Mod Info mod_name=DistantHorizons +mod_id=distanthorizons mod_version=2.0.2-a-dev api_version=1.1.0 maven_group=com.seibel.distanthorizons diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e6aba2515..1af9e0930 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle b/settings.gradle index 5e8900e42..f90316ec2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,6 +25,10 @@ pluginManagement { name "Sponge" url "https://repo.spongepowered.org/repository/maven-public/" } + maven { + name "ParchmentMC" + url "https://maven.parchmentmc.org" + } mavenCentral() gradlePluginPortal() diff --git a/versionProperties/1.20.1.properties b/versionProperties/1.20.1.properties index ba4d8188b..c09f670e1 100644 --- a/versionProperties/1.20.1.properties +++ b/versionProperties/1.20.1.properties @@ -2,6 +2,7 @@ java_version=17 minecraft_version=1.20.1 parchment_version=1.20.1:2023.09.03 +parchment_forge_version=1.20.1-2023.09.03 compatible_minecraft_versions=["1.20", "1.20.1"] accessWidenerVersion=1_20 builds_for=fabric,forge diff --git a/versionProperties/1.20.2.properties b/versionProperties/1.20.2.properties index 1f15abab5..20b716662 100644 --- a/versionProperties/1.20.2.properties +++ b/versionProperties/1.20.2.properties @@ -2,6 +2,7 @@ java_version=17 minecraft_version=1.20.2 parchment_version=1.20.1:2023.09.03 +parchment_forge_version=1.20.1-2023.09.03 compatible_minecraft_versions=["1.20.2"] accessWidenerVersion=1_20_2 builds_for=fabric,forge diff --git a/versionProperties/1.20.4.properties b/versionProperties/1.20.4.properties index 1f588d1db..74ac59e5a 100644 --- a/versionProperties/1.20.4.properties +++ b/versionProperties/1.20.4.properties @@ -2,9 +2,10 @@ java_version=17 minecraft_version=1.20.4 parchment_version=1.20.2:2023.12.10 +parchment_forge_version=1.20.2-2023.12.10 compatible_minecraft_versions=["1.20.3", "1.20.4"] accessWidenerVersion=1_20_2 -builds_for=fabric,forge,neoforge +builds_for=forge,fabric # Fabric loader fabric_loader_version=0.15.1