|
| 1 | +/* |
| 2 | + * Copyright (c) Forge Development LLC and contributors |
| 3 | + * SPDX-License-Identifier: LGPL-2.1-only |
| 4 | + */ |
| 5 | +package net.minecraftforge.gradle.internal; |
| 6 | + |
| 7 | +import org.jspecify.annotations.NullUnmarked; |
| 8 | + |
| 9 | +import java.util.regex.Pattern; |
| 10 | + |
| 11 | +@NullUnmarked |
| 12 | +record ParchmentVersion( |
| 13 | + String timestamp, |
| 14 | + /* @Nullable */ String mcVersion, |
| 15 | + /* @Nullable */ String mapMcVersion |
| 16 | +) { |
| 17 | + private static final Pattern TIMESTAMP = Pattern.compile("\\d{4}.\\d{2}.\\d{2}"); |
| 18 | + private static final Pattern MCP_TIMESTAMP = Pattern.compile("\\d{8}.\\d{6}"); |
| 19 | + |
| 20 | + // Parchment related things |
| 21 | + public static final String PARCHMENT_MAVEN = "https://maven.parchmentmc.org/"; |
| 22 | + private static final String PARCHMENT_GROUP = "org.parchmentmc.data"; // Name is "parchment-{mcversion}' |
| 23 | + |
| 24 | + /** |
| 25 | + * <pre> |
| 26 | + * Parchment names can be specified in many variants, including some 'shorthand's |
| 27 | + * Basing this implementation on https://parchmentmc.org/docs/getting-started |
| 28 | + * |
| 29 | + * Namely, (in case they change the site) |
| 30 | + * |
| 31 | + * For using Parchment on the same version of Minecraft: |
| 32 | + * YYYY.MM.DD-<Environment MC version> |
| 33 | + * Examples: |
| 34 | + * 2021.12.12-1.17.1 for Minecraft 1.17.1 |
| 35 | + * 2022.08.07-1.18.2 for Minecraft 1.18.2 |
| 36 | + * 2022.08.14-1.19.2 for Minecraft 1.19.2 |
| 37 | + * |
| 38 | + * For using Parchment for an older version on a newer MC version |
| 39 | + * <Mapping's MC version>-YYYY.MM.DD-<Environment MC version> |
| 40 | + * Examples: |
| 41 | + * 1.17.1-2021.12.12-1.18 |
| 42 | + * Minecraft 1.17.1 mappings (2021.12.12-1.17.1) in an MC 1.18 environment |
| 43 | + * 1.18.2-2022.08.07-1.19.1 |
| 44 | + * Minecraft 1.18.2 mappings (2021.08.07-1.18.2) in an MC 1.19.1 environment |
| 45 | + * 1.19.2-2022.08.14-1.20 |
| 46 | + * Minecraft 1.19.2 mappings (2021.08.14-1.19.2) in an MC 1.20 environment |
| 47 | + * |
| 48 | + * Parchment also publishes 'snapshots', These are not supported by MCMaven, and as such will |
| 49 | + * throw an exception during parsing. |
| 50 | + * Example: |
| 51 | + * parchment-1.21.9/2025.10.05-nightly-SNAPSHOT |
| 52 | + * parchment-1.16.5/BLEEDING-SNAPSHOT |
| 53 | + * They have also published two known versions that don't follow the standard format. And I don't feel like supporting it. |
| 54 | + * parchment-1.16.5/20210607-SNAPSHOT |
| 55 | + * parchment-1.16.5/20210608-SNAPSHOT |
| 56 | + * |
| 57 | + * Tho undocumented, the implementation of Parchment's FG6 plugin supported specifying the MCP timestamp as part of the Environment MC version |
| 58 | + * https://github.com/ParchmentMC/Librarian/blob/c7f9878feb76d210aa65569fe38cad5297f439c5/src/main/java/org/parchmentmc/librarian/forgegradle/ParchmentMappingVersion.java#L52 |
| 59 | + * |
| 60 | + * In addition to the above formats I DO support not specifying a Minecraft version. It will need to be filled in later |
| 61 | + * to find the correct download, but typically we can pull it from MCPConfig/other context. |
| 62 | + * |
| 63 | + * Note: This does not do any case sanitization |
| 64 | + * |
| 65 | + * @throws IllegalArgumentException if the version fails to parse |
| 66 | + */ |
| 67 | + public static ParchmentVersion parse(String version) { |
| 68 | + if (version == null) |
| 69 | + throw new IllegalArgumentException("Parchment mappings version must be present"); |
| 70 | + |
| 71 | + if (version.contains("-SNAPSHOT")) |
| 72 | + throw new IllegalArgumentException("Parchment snapshots are not supported: " + version); |
| 73 | + |
| 74 | + var matcher = TIMESTAMP.matcher(version); |
| 75 | + if (!matcher.find()) |
| 76 | + throw new IllegalArgumentException("Parchment version does not contain a timestamp: " + version); |
| 77 | + |
| 78 | + int start = matcher.start(); |
| 79 | + int end = matcher.end(); |
| 80 | + |
| 81 | + // Simple case, we just specify the timestamp |
| 82 | + if (start == 0 && end == version.length()) |
| 83 | + return new ParchmentVersion(version, null, null); |
| 84 | + |
| 85 | + var timestamp = matcher.group(); |
| 86 | + String mcVersion = null; |
| 87 | + |
| 88 | + // Minecraft Version is specified |
| 89 | + if (end < version.length()) { |
| 90 | + if (version.charAt(end) != '-' || end == version.length() - 1) |
| 91 | + throw new IllegalArgumentException("Parchment version does not specify Minecraft version: " + version); |
| 92 | + mcVersion = version.substring(end + 1); |
| 93 | + } |
| 94 | + |
| 95 | + // Default mapping version is the same as mc version |
| 96 | + var mapMcVersion = stripMcp(mcVersion); |
| 97 | + |
| 98 | + // Mappings MC version is specified |
| 99 | + if (start > 0) { |
| 100 | + if (version.charAt(start - 1) != '-' || start == 1) |
| 101 | + throw new IllegalArgumentException("Parchment version does not specify Mapping Minecraft version: " + version); |
| 102 | + mapMcVersion = version.substring(0, start - 1); |
| 103 | + } |
| 104 | + |
| 105 | + return new ParchmentVersion(timestamp, mcVersion, mapMcVersion); |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * Variant of {@link #parse(String)} that returns null instead of throwing IllegalArgumentException |
| 110 | + */ |
| 111 | + public static ParchmentVersion tryParse(String version) { |
| 112 | + try { |
| 113 | + return parse(version); |
| 114 | + } catch (IllegalArgumentException e) { |
| 115 | + return null; |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + // Remove MCP timestamp if its on there. |
| 120 | + private static String stripMcp(String version) { |
| 121 | + if (version == null) |
| 122 | + return null; |
| 123 | + if (version.length() <= 17 || version.charAt(version.length() - 16) != '-') |
| 124 | + return version; |
| 125 | + |
| 126 | + var matcher = MCP_TIMESTAMP.matcher(version); |
| 127 | + if (matcher.find() && matcher.start() == version.length() - 15) |
| 128 | + return version.substring(0, version.length() - 16); |
| 129 | + |
| 130 | + return version; |
| 131 | + } |
| 132 | + |
| 133 | + public ParchmentVersion withMinecraft(String mcVersion) { |
| 134 | + return new ParchmentVersion(timestamp, mcVersion, mapMcVersion == null ? mcVersion : mapMcVersion); |
| 135 | + } |
| 136 | + |
| 137 | + public String toFriendly() { |
| 138 | + if (mapMcVersion == null) { |
| 139 | + if (mcVersion == null) |
| 140 | + return timestamp; |
| 141 | + return timestamp + '-' + mcVersion; |
| 142 | + } |
| 143 | + |
| 144 | + if (mcVersion == null) |
| 145 | + return mapMcVersion + '-' + timestamp; |
| 146 | + |
| 147 | + if (mapMcVersion.equals(stripMcp(mcVersion))) |
| 148 | + return timestamp + '-' + mcVersion; |
| 149 | + |
| 150 | + return mapMcVersion + '-' + timestamp + '-' + mcVersion; |
| 151 | + } |
| 152 | +} |
0 commit comments