Compare commits
	
		
			2 Commits
		
	
	
		
			edd5b06a46
			...
			e6fc31e5ca
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e6fc31e5ca | |||
| 70c4d59fdc | 
| @@ -85,6 +85,27 @@ | |||||||
|                     </execution> |                     </execution> | ||||||
|                 </executions> |                 </executions> | ||||||
|             </plugin> |             </plugin> | ||||||
|  |  | ||||||
|  |             <plugin> | ||||||
|  |                 <groupId>com.googlecode.maven-download-plugin</groupId> | ||||||
|  |                 <artifactId>download-maven-plugin</artifactId> | ||||||
|  |                 <version>1.7.0</version> | ||||||
|  |                 <executions> | ||||||
|  |                     <execution> | ||||||
|  |                         <id>mcversion-download</id> | ||||||
|  |                         <phase>compile</phase> | ||||||
|  |                         <goals> | ||||||
|  |                             <goal>wget</goal> | ||||||
|  |                         </goals> | ||||||
|  |                     </execution> | ||||||
|  |                 </executions> | ||||||
|  |                 <configuration> | ||||||
|  |                     <url>https://api.pandacube.fr/rest/mcversion</url> | ||||||
|  |                     <outputDirectory>${project.basedir}/src/main/resources/fr/pandacube/lib/core/mc_version</outputDirectory> | ||||||
|  |                     <outputFileName>mcversion.json</outputFileName> | ||||||
|  |                     <skipCache>true</skipCache> | ||||||
|  |                 </configuration> | ||||||
|  |             </plugin> | ||||||
|         </plugins> |         </plugins> | ||||||
|     </build> |     </build> | ||||||
| </project> | </project> | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ public class CronScheduler { | |||||||
|                     CronTask next = tasks.get(0); |                     CronTask next = tasks.get(0); | ||||||
|                     if (next.nextRun <= now) { |                     if (next.nextRun <= now) { | ||||||
|                         next.runAsync(); |                         next.runAsync(); | ||||||
|                         setLastRun(next.taskId, next.nextRun); |                         setLastRun(next.taskId, now); | ||||||
|                         onTaskUpdate(false); |                         onTaskUpdate(false); | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|   | |||||||
| @@ -4,15 +4,23 @@ import fr.pandacube.lib.util.StringUtil; | |||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import java.util.TreeSet; | import java.util.TreeSet; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Utility class to manipulate {@link String}s representing Minecraft versions. | ||||||
|  |  */ | ||||||
| public class MinecraftVersionUtil { | public class MinecraftVersionUtil { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Compare two Minecraft version strings. It uses the rules of semantic | ||||||
|  |      * versionning to compare the versions. | ||||||
|  |      * @param v1 the first version to compare. | ||||||
|  |      * @param v2 the second version to compare. | ||||||
|  |      * @return 0 if they are equal, <0 if v1<v2 and vice-versa. | ||||||
|  |      */ | ||||||
|     public static int compareVersions(String v1, String v2) { |     public static int compareVersions(String v1, String v2) { | ||||||
|         int[] v1Int = decomposedVersion(v1); |         int[] v1Int = decomposedVersion(v1); | ||||||
|         int[] v2Int = decomposedVersion(v2); |         int[] v2Int = decomposedVersion(v2); | ||||||
| @@ -27,7 +35,11 @@ public class MinecraftVersionUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Decompose a version string into a series of integers. | ||||||
|  |      * @param v a string representation of a version (eg. 1.19.1). | ||||||
|  |      * @return an array of int representing the provided version (eg. [1, 19, 1]). | ||||||
|  |      */ | ||||||
|     public static int[] decomposedVersion(String v) { |     public static int[] decomposedVersion(String v) { | ||||||
|         try { |         try { | ||||||
|             return Arrays.stream(v.split("\\.")).mapToInt(Integer::parseInt).toArray(); |             return Arrays.stream(v.split("\\.")).mapToInt(Integer::parseInt).toArray(); | ||||||
| @@ -36,7 +48,20 @@ public class MinecraftVersionUtil { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Tells if the two provided Minecraft versions are consecutives. | ||||||
|  |      * <p> | ||||||
|  |      * Two versions are consecutives if (considering {@code 1.X[.Y]}): | ||||||
|  |      * <ul> | ||||||
|  |      *     <li>They are part of the same main version (X value)</li> | ||||||
|  |      *     <li>v1 has no Y value, and v2 has Y = 1 (eg. 1.19 and 1.19.1) OR | ||||||
|  |      *         both v1 and v2 has a Y value and those values are consecutives. | ||||||
|  |      *     </li> | ||||||
|  |      * </ul> | ||||||
|  |      * @param v1 the first version. | ||||||
|  |      * @param v2 the second version. | ||||||
|  |      * @return thue if the second version is consecutive to the first one. | ||||||
|  |      */ | ||||||
|     public static boolean areConsecutives(String v1, String v2) { |     public static boolean areConsecutives(String v1, String v2) { | ||||||
|         int[] v1Int = decomposedVersion(v1); |         int[] v1Int = decomposedVersion(v1); | ||||||
|         int[] v2Int = decomposedVersion(v2); |         int[] v2Int = decomposedVersion(v2); | ||||||
| @@ -62,6 +87,17 @@ public class MinecraftVersionUtil { | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Generate a string representation of the provided list of version, with | ||||||
|  |      * merged consecutive versions and using | ||||||
|  |      * {@link StringUtil#joinGrammatically(CharSequence, CharSequence, List)}. | ||||||
|  |      * | ||||||
|  |      * @param versions the minecraft versions list to use. | ||||||
|  |      * @param finalWordSeparator the word separator between the two last versions in the returned string, like "and", | ||||||
|  |      *                           "or" or any other word of any language. The spaces before and after are already | ||||||
|  |      *                           concatenated. | ||||||
|  |      * @return a string representation of the provided list of version. | ||||||
|  |      */ | ||||||
|     public static String toString(List<String> versions, String finalWordSeparator) { |     public static String toString(List<String> versions, String finalWordSeparator) { | ||||||
|         if (versions.isEmpty()) |         if (versions.isEmpty()) | ||||||
|             return ""; |             return ""; | ||||||
|   | |||||||
| @@ -2,7 +2,8 @@ package fr.pandacube.lib.core.mc_version; | |||||||
|  |  | ||||||
| import fr.pandacube.lib.core.json.Json; | import fr.pandacube.lib.core.json.Json; | ||||||
| import fr.pandacube.lib.util.Log; | import fr.pandacube.lib.util.Log; | ||||||
| import org.jetbrains.annotations.ApiStatus.Internal; | import fr.pandacube.lib.util.MinecraftVersion; | ||||||
|  | import fr.pandacube.lib.util.StringUtil; | ||||||
|  |  | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||||
| @@ -11,9 +12,22 @@ import java.net.http.HttpClient; | |||||||
| import java.net.http.HttpRequest; | import java.net.http.HttpRequest; | ||||||
| import java.net.http.HttpResponse; | import java.net.http.HttpResponse; | ||||||
| import java.net.http.HttpResponse.BodyHandlers; | import java.net.http.HttpResponse.BodyHandlers; | ||||||
|  | import java.time.Duration; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.concurrent.atomic.AtomicReference; | import java.util.concurrent.atomic.AtomicReference; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Class handling a relationship table of known Minecraft version and their | ||||||
|  |  * corresponding protocol version. | ||||||
|  |  * <p> | ||||||
|  |  * The data if fetch updated data from an external API on startup. If it fails, | ||||||
|  |  * it uses the data stored in the current package at build time. | ||||||
|  |  * <p> | ||||||
|  |  * The public static methos are used to fetch an instance of {@link ProtocolVersion} | ||||||
|  |  * based on the provided protocol version (eg. 763) or Minecraft version (eg. "1.20.1"). | ||||||
|  |  * An instance of this class provides information related to a protocol version | ||||||
|  |  * (the protocol version number and all the corresponding Minecraft versions). | ||||||
|  |  */ | ||||||
| public class ProtocolVersion { | public class ProtocolVersion { | ||||||
|  |  | ||||||
|     private static final String ONLINE_DATA_URL = "https://api.pandacube.fr/rest/mcversion"; |     private static final String ONLINE_DATA_URL = "https://api.pandacube.fr/rest/mcversion"; | ||||||
| @@ -28,10 +42,25 @@ public class ProtocolVersion { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Replace the currently used data cache by a new source. | ||||||
|  |      * <p> | ||||||
|  |      * <b>Note: </b>this method is not meant to be used by the final user of | ||||||
|  |      * this class. Use it only if you have a better data source. | ||||||
|  |      * @param data the data to use instead of the provided (external API or packaged file) | ||||||
|  |      */ | ||||||
|     public static void setRawData(MinecraftVersionList data) { |     public static void setRawData(MinecraftVersionList data) { | ||||||
|         versionList.set(data); |         versionList.set(data); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Gets the raw data used internally by this class. | ||||||
|  |      * <p> | ||||||
|  |      * <b>Note: </b>this method is not meant to be used by the final user of | ||||||
|  |      * this class. Use it only if you know what you do. | ||||||
|  |      * @return the current instance of {@link MinecraftVersionUtil} uses | ||||||
|  |      *         internally by this class. | ||||||
|  |      */ | ||||||
|     public static MinecraftVersionList getRawData() { |     public static MinecraftVersionList getRawData() { | ||||||
|         initIfNecessary(); |         initIfNecessary(); | ||||||
|         return versionList.get(); |         return versionList.get(); | ||||||
| @@ -40,7 +69,9 @@ public class ProtocolVersion { | |||||||
|     private static void init() { |     private static void init() { | ||||||
|         // try online source first |         // try online source first | ||||||
|         try { |         try { | ||||||
|             HttpResponse<String> response = HttpClient.newHttpClient() |             HttpResponse<String> response = HttpClient.newBuilder() | ||||||
|  |                     .connectTimeout(Duration.ofSeconds(5)) | ||||||
|  |                     .build() | ||||||
|                     .send(HttpRequest.newBuilder(URI.create(ONLINE_DATA_URL)).build(), |                     .send(HttpRequest.newBuilder(URI.create(ONLINE_DATA_URL)).build(), | ||||||
|                             BodyHandlers.ofString() |                             BodyHandlers.ofString() | ||||||
|                     ); |                     ); | ||||||
| @@ -70,19 +101,96 @@ public class ProtocolVersion { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     private static int getPVNOfVersion(String version) { | ||||||
|  |  | ||||||
|     public static int ofVersion(String versionId) { |  | ||||||
|         initIfNecessary(); |         initIfNecessary(); | ||||||
|         Integer v = versionList.get().protocolOfVersion().get(versionId); |         Integer v = versionList.get().protocolOfVersion().get(version); | ||||||
|         return v == null ? -1 : v; |         return v == null ? -1 : v; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private static List<String> getVersionsOfPVN(int pvn) { | ||||||
|     public static List<String> getVersionsOfProtocol(int protocolVersion) { |  | ||||||
|         initIfNecessary(); |         initIfNecessary(); | ||||||
|         return versionList.get().versionsOfProtocol().get(protocolVersion); |         return versionList.get().versionsOfProtocol().get(pvn); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Gets the {@link ProtocolVersion} associated with the provided Minecraft version. | ||||||
|  |      * @param version The Minecraft version, in the format "X.X[.X]" (eg. "1.17" or "1.8.8"). | ||||||
|  |      * @return an instance of {@link ProtocolVersion}. | ||||||
|  |      */ | ||||||
|  |     public static ProtocolVersion ofVersion(String version) { | ||||||
|  |         int pvn = getPVNOfVersion(version); | ||||||
|  |         if (pvn == -1) | ||||||
|  |             return null; | ||||||
|  |         List<String> versions = getVersionsOfPVN(pvn); | ||||||
|  |         if (versions == null) { | ||||||
|  |             versions = List.of(version); | ||||||
|  |         } | ||||||
|  |         return new ProtocolVersion(pvn, List.copyOf(versions)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Gets the {@link ProtocolVersion} associated with the provided protocol version number. | ||||||
|  |      * @param pvn The protocol version number. | ||||||
|  |      * @return an instance of {@link ProtocolVersion}. | ||||||
|  |      */ | ||||||
|  |     public static ProtocolVersion ofProtocol(int pvn) { | ||||||
|  |         List<String> versions = getVersionsOfPVN(pvn); | ||||||
|  |         if (versions == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return new ProtocolVersion(pvn, List.copyOf(versions)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The protocol version number. | ||||||
|  |      */ | ||||||
|  |     public final int protocolVersionNumber; | ||||||
|  |     /** | ||||||
|  |      * All Minecraft version supported by this protocol version number. | ||||||
|  |      */ | ||||||
|  |     public final List<String> versions; | ||||||
|  |  | ||||||
|  |     private ProtocolVersion(int protocolVersionNumber, List<String> versions) { | ||||||
|  |         this.protocolVersionNumber = protocolVersionNumber; | ||||||
|  |         this.versions = versions; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return "ProtocolVersion{protocol=" + protocolVersionNumber + ", toString(\"and\")=" + toString("and") + "}"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns a string representation of all the Minecraft version of this enum value, using | ||||||
|  |      * {@link StringUtil#joinGrammatically(CharSequence, CharSequence, List)}. | ||||||
|  |      * | ||||||
|  |      * @param finalWordSeparator the word separator between the two last versions in the returned string, like "and", | ||||||
|  |      *                           "or" or any other word of any language. The spaces before and after are already | ||||||
|  |      *                           concatenated. | ||||||
|  |      * @return a string representation of this {@link ProtocolVersion}. | ||||||
|  |      */ | ||||||
|  |     public String toString(String finalWordSeparator) { | ||||||
|  |         return displayOptimizedListOfVersions(List.of(this), finalWordSeparator); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Generate a string representation of the provided list of version, using | ||||||
|  |      * {@link StringUtil#joinGrammatically(CharSequence, CharSequence, List)}. | ||||||
|  |      * | ||||||
|  |      * @param versions the minecraft versions to list | ||||||
|  |      * @param finalWordSeparator the word separator between the two last versions in the returned string, like "and", | ||||||
|  |      *                           "or" or any other word of any language. The spaces before and after are already | ||||||
|  |      *                           concatenated. | ||||||
|  |      * @return a string representation of the provided list of version. | ||||||
|  |      */ | ||||||
|  |     public static String displayOptimizedListOfVersions(List<ProtocolVersion> versions, String finalWordSeparator) { | ||||||
|  |         return MinecraftVersionUtil.toString(versions.stream().flatMap(pv -> pv.versions.stream()).toList(), finalWordSeparator); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,206 @@ | |||||||
|  | { | ||||||
|  |   "protocolOfVersion": { | ||||||
|  |     "1.20.1": 763, | ||||||
|  |     "1.20": 763, | ||||||
|  |     "1.19.4": 762, | ||||||
|  |     "1.19.3": 761, | ||||||
|  |     "1.19.2": 760, | ||||||
|  |     "1.19.1": 760, | ||||||
|  |     "1.19": 759, | ||||||
|  |     "1.18.2": 758, | ||||||
|  |     "1.18.1": 757, | ||||||
|  |     "1.18": 757, | ||||||
|  |     "1.17.1": 756, | ||||||
|  |     "1.17": 755, | ||||||
|  |     "1.16.5": 754, | ||||||
|  |     "1.16.4": 754, | ||||||
|  |     "1.16.3": 753, | ||||||
|  |     "1.16.2": 751, | ||||||
|  |     "1.16.1": 736, | ||||||
|  |     "1.16": 735, | ||||||
|  |     "1.15.2": 578, | ||||||
|  |     "1.15.1": 575, | ||||||
|  |     "1.15": 573, | ||||||
|  |     "1.14.4": 498, | ||||||
|  |     "1.14.3": 490, | ||||||
|  |     "1.14.2": 485, | ||||||
|  |     "1.14.1": 480, | ||||||
|  |     "1.14": 477, | ||||||
|  |     "1.13.2": 404, | ||||||
|  |     "1.13.1": 401, | ||||||
|  |     "1.13": 393, | ||||||
|  |     "1.12.2": 340, | ||||||
|  |     "1.12.1": 338, | ||||||
|  |     "1.12": 335, | ||||||
|  |     "1.11.2": 316, | ||||||
|  |     "1.11.1": 316, | ||||||
|  |     "1.11": 315, | ||||||
|  |     "1.10.2": 210, | ||||||
|  |     "1.10.1": 210, | ||||||
|  |     "1.10": 210, | ||||||
|  |     "1.9.4": 110, | ||||||
|  |     "1.9.3": 110, | ||||||
|  |     "1.9.2": 109, | ||||||
|  |     "1.9.1": 108, | ||||||
|  |     "1.9": 107, | ||||||
|  |     "1.8.9": 47, | ||||||
|  |     "1.8.8": 47, | ||||||
|  |     "1.8.7": 47, | ||||||
|  |     "1.8.6": 47, | ||||||
|  |     "1.8.5": 47, | ||||||
|  |     "1.8.4": 47, | ||||||
|  |     "1.8.3": 47, | ||||||
|  |     "1.8.2": 47, | ||||||
|  |     "1.8.1": 47, | ||||||
|  |     "1.8": 47, | ||||||
|  |     "1.7.10": 5, | ||||||
|  |     "1.7.9": 5, | ||||||
|  |     "1.7.8": 5, | ||||||
|  |     "1.7.7": 5, | ||||||
|  |     "1.7.6": 5, | ||||||
|  |     "1.7.5": 4, | ||||||
|  |     "1.7.4": 4, | ||||||
|  |     "1.7.3": 4, | ||||||
|  |     "1.7.2": 4 | ||||||
|  |   }, | ||||||
|  |   "versionsOfProtocol": { | ||||||
|  |     "763": [ | ||||||
|  |       "1.20", | ||||||
|  |       "1.20.1" | ||||||
|  |     ], | ||||||
|  |     "762": [ | ||||||
|  |       "1.19.4" | ||||||
|  |     ], | ||||||
|  |     "761": [ | ||||||
|  |       "1.19.3" | ||||||
|  |     ], | ||||||
|  |     "760": [ | ||||||
|  |       "1.19.1", | ||||||
|  |       "1.19.2" | ||||||
|  |     ], | ||||||
|  |     "759": [ | ||||||
|  |       "1.19" | ||||||
|  |     ], | ||||||
|  |     "758": [ | ||||||
|  |       "1.18.2" | ||||||
|  |     ], | ||||||
|  |     "757": [ | ||||||
|  |       "1.18", | ||||||
|  |       "1.18.1" | ||||||
|  |     ], | ||||||
|  |     "756": [ | ||||||
|  |       "1.17.1" | ||||||
|  |     ], | ||||||
|  |     "755": [ | ||||||
|  |       "1.17" | ||||||
|  |     ], | ||||||
|  |     "754": [ | ||||||
|  |       "1.16.4", | ||||||
|  |       "1.16.5" | ||||||
|  |     ], | ||||||
|  |     "753": [ | ||||||
|  |       "1.16.3" | ||||||
|  |     ], | ||||||
|  |     "751": [ | ||||||
|  |       "1.16.2" | ||||||
|  |     ], | ||||||
|  |     "736": [ | ||||||
|  |       "1.16.1" | ||||||
|  |     ], | ||||||
|  |     "735": [ | ||||||
|  |       "1.16" | ||||||
|  |     ], | ||||||
|  |     "578": [ | ||||||
|  |       "1.15.2" | ||||||
|  |     ], | ||||||
|  |     "575": [ | ||||||
|  |       "1.15.1" | ||||||
|  |     ], | ||||||
|  |     "573": [ | ||||||
|  |       "1.15" | ||||||
|  |     ], | ||||||
|  |     "498": [ | ||||||
|  |       "1.14.4" | ||||||
|  |     ], | ||||||
|  |     "490": [ | ||||||
|  |       "1.14.3" | ||||||
|  |     ], | ||||||
|  |     "485": [ | ||||||
|  |       "1.14.2" | ||||||
|  |     ], | ||||||
|  |     "480": [ | ||||||
|  |       "1.14.1" | ||||||
|  |     ], | ||||||
|  |     "477": [ | ||||||
|  |       "1.14" | ||||||
|  |     ], | ||||||
|  |     "404": [ | ||||||
|  |       "1.13.2" | ||||||
|  |     ], | ||||||
|  |     "401": [ | ||||||
|  |       "1.13.1" | ||||||
|  |     ], | ||||||
|  |     "393": [ | ||||||
|  |       "1.13" | ||||||
|  |     ], | ||||||
|  |     "340": [ | ||||||
|  |       "1.12.2" | ||||||
|  |     ], | ||||||
|  |     "338": [ | ||||||
|  |       "1.12.1" | ||||||
|  |     ], | ||||||
|  |     "335": [ | ||||||
|  |       "1.12" | ||||||
|  |     ], | ||||||
|  |     "316": [ | ||||||
|  |       "1.11.1", | ||||||
|  |       "1.11.2" | ||||||
|  |     ], | ||||||
|  |     "315": [ | ||||||
|  |       "1.11" | ||||||
|  |     ], | ||||||
|  |     "210": [ | ||||||
|  |       "1.10", | ||||||
|  |       "1.10.1", | ||||||
|  |       "1.10.2" | ||||||
|  |     ], | ||||||
|  |     "110": [ | ||||||
|  |       "1.9.3", | ||||||
|  |       "1.9.4" | ||||||
|  |     ], | ||||||
|  |     "109": [ | ||||||
|  |       "1.9.2" | ||||||
|  |     ], | ||||||
|  |     "108": [ | ||||||
|  |       "1.9.1" | ||||||
|  |     ], | ||||||
|  |     "107": [ | ||||||
|  |       "1.9" | ||||||
|  |     ], | ||||||
|  |     "47": [ | ||||||
|  |       "1.8", | ||||||
|  |       "1.8.1", | ||||||
|  |       "1.8.2", | ||||||
|  |       "1.8.3", | ||||||
|  |       "1.8.4", | ||||||
|  |       "1.8.5", | ||||||
|  |       "1.8.6", | ||||||
|  |       "1.8.7", | ||||||
|  |       "1.8.8", | ||||||
|  |       "1.8.9" | ||||||
|  |     ], | ||||||
|  |     "5": [ | ||||||
|  |       "1.7.6", | ||||||
|  |       "1.7.7", | ||||||
|  |       "1.7.8", | ||||||
|  |       "1.7.9", | ||||||
|  |       "1.7.10" | ||||||
|  |     ], | ||||||
|  |     "4": [ | ||||||
|  |       "1.7.2", | ||||||
|  |       "1.7.3", | ||||||
|  |       "1.7.4", | ||||||
|  |       "1.7.5" | ||||||
|  |     ] | ||||||
|  |   } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user