1+ /*
2+ * This file is part of helper, licensed under the MIT License.
3+ *
4+ * Copyright (c) lucko (Luck) <luck@lucko.me>
5+ * Copyright (c) contributors
6+ *
7+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8+ * of this software and associated documentation files (the "Software"), to deal
9+ * in the Software without restriction, including without limitation the rights
10+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+ * copies of the Software, and to permit persons to whom the Software is
12+ * furnished to do so, subject to the following conditions:
13+ *
14+ * The above copyright notice and this permission notice shall be included in all
15+ * copies or substantial portions of the Software.
16+ *
17+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+ * SOFTWARE.
24+ */
25+
26+ package com .smc .version ;
27+
28+ import org .jetbrains .annotations .NotNull ;
29+ import org .jetbrains .annotations .Nullable ;
30+
31+ import java .text .SimpleDateFormat ;
32+ import java .util .Comparator ;
33+ import java .util .Locale ;
34+ import java .util .Objects ;
35+
36+ /**
37+ * Encapsulates a version of Minecraft.
38+ *
39+ * @author Kristian (ProtocolLib)
40+ */
41+ @ SuppressWarnings ("unused" )
42+ public final class MinecraftVersion implements Comparable <MinecraftVersion > {
43+
44+ public static final Comparator <MinecraftVersion > COMPARATOR = Comparator .nullsFirst (Comparator
45+ .comparingInt (MinecraftVersion ::getMajor )
46+ .thenComparingInt (MinecraftVersion ::getMinor )
47+ .thenComparingInt (MinecraftVersion ::getBuild )
48+ .thenComparing (Comparator .nullsLast (Comparator .comparing (MinecraftVersion ::getDevelopmentStage )))
49+ .thenComparing (Comparator .nullsFirst (Comparator .comparing (MinecraftVersion ::getSnapshot )))
50+ );
51+
52+ /**
53+ * The newest known version of Minecraft
54+ */
55+ private static final String NEWEST_MINECRAFT_VERSION = "1.13.1" ;
56+
57+ /**
58+ * The date (with ISO 8601 or YYYY-MM-DD) when the most recent version was released.
59+ */
60+ private static final String MINECRAFT_LAST_RELEASE_DATE = "2018-08-22" ;
61+
62+ /**
63+ * Gets the {@link MinecraftVersion} of the runtime server.
64+ *
65+ * @return the runtime minecraft version.
66+ */
67+ public static MinecraftVersion getRuntimeVersion () {
68+ return MinecraftVersions .RUNTIME_VERSION ;
69+ }
70+
71+ /**
72+ * Creates a new {@link MinecraftVersion} with the given properties.
73+ *
74+ * @param major the major component
75+ * @param minor the minor component
76+ * @param build the build component
77+ * @return a version instance
78+ */
79+ public static MinecraftVersion of (int major , int minor , int build ) {
80+ return new MinecraftVersion (major , minor , build , null , null );
81+ }
82+
83+ /**
84+ * Parses a {@link MinecraftVersion} from a version string, in the format
85+ * <code>major.minor.build</code>, or in the snapshot format.
86+ *
87+ * @param version the version in text form.
88+ * @throws IllegalArgumentException if unable to parse
89+ */
90+ public static MinecraftVersion parse (String version ) throws IllegalArgumentException {
91+ return parse (version , true );
92+ }
93+
94+ /**
95+ * Parses a {@link MinecraftVersion} from a version string, in the format
96+ * <code>major.minor.build</code>, or in the snapshot format.
97+ *
98+ * @param version the version in text form.
99+ * @param parseSnapshot if the implementation should try to parse a snapshot version
100+ * @throws IllegalArgumentException if unable to parse
101+ */
102+ public static MinecraftVersion parse (String version , boolean parseSnapshot ) throws IllegalArgumentException {
103+ String [] parts = version .split ("-" );
104+ SnapshotVersion snapshot = null ;
105+ int [] versionComponents = new int [3 ];
106+
107+ try {
108+ versionComponents = parseVersion (parts [0 ]);
109+ } catch (NumberFormatException cause ) {
110+ // Skip snapshot parsing
111+ if (!parseSnapshot )
112+ throw cause ;
113+
114+ try {
115+ // Determine if the snapshot is newer than the current release version
116+ snapshot = SnapshotVersion .parse (parts [0 ]);
117+ SimpleDateFormat format = new SimpleDateFormat ("yyyy-MM-dd" , Locale .US );
118+
119+ MinecraftVersion latest = MinecraftVersion .parse (NEWEST_MINECRAFT_VERSION , false );
120+ boolean newer = snapshot .getSnapshotDate ().compareTo (format .parse (MINECRAFT_LAST_RELEASE_DATE )) > 0 ;
121+
122+ versionComponents [0 ] = latest .getMajor ();
123+ versionComponents [1 ] = latest .getMinor () + (newer ? 1 : -1 );
124+ } catch (Exception e ) {
125+ throw new IllegalArgumentException ("Cannot parse " + parts [0 ], e );
126+ }
127+ }
128+
129+ int major = versionComponents [0 ];
130+ int minor = versionComponents [1 ];
131+ int build = versionComponents [2 ];
132+ String development = parts .length > 1 ? parts [1 ] : (snapshot != null ? "snapshot" : null );
133+ return new MinecraftVersion (major , minor , build , development , snapshot );
134+ }
135+
136+ private static int [] parseVersion (String version ) {
137+ String [] elements = version .split ("\\ ." );
138+ int [] numbers = new int [3 ];
139+
140+ // Make sure it's even a valid version
141+ if (elements .length < 1 ) {
142+ throw new IllegalStateException ("Corrupt MC version: " + version );
143+ }
144+
145+ // The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
146+ for (int i = 0 ; i < Math .min (numbers .length , elements .length ); i ++) {
147+ numbers [i ] = Integer .parseInt (elements [i ].trim ());
148+ }
149+ return numbers ;
150+ }
151+
152+
153+ private final int major ;
154+ private final int minor ;
155+ private final int build ;
156+
157+ // The development stage
158+ @ Nullable
159+ private final String development ;
160+
161+ // Snapshot?
162+ @ Nullable
163+ private final SnapshotVersion snapshot ;
164+
165+ /**
166+ * Construct a version object.
167+ *
168+ * @param major - major version number.
169+ * @param minor - minor version number.
170+ * @param build - build version number.
171+ * @param development - development stage.
172+ */
173+ private MinecraftVersion (int major , int minor , int build , @ Nullable String development , @ Nullable SnapshotVersion snapshot ) {
174+ this .major = major ;
175+ this .minor = minor ;
176+ this .build = build ;
177+ this .development = development ;
178+ this .snapshot = snapshot ;
179+ }
180+
181+ /**
182+ * Gets the major component of the version.
183+ *
184+ * @return the major component of the version
185+ */
186+ public int getMajor () {
187+ return this .major ;
188+ }
189+
190+ /**
191+ * Gets the minor component of the version.
192+ *
193+ * @return the minor component of the version.
194+ */
195+ public int getMinor () {
196+ return this .minor ;
197+ }
198+
199+ /**
200+ * Gets the build component of the version.
201+ *
202+ * @return the build component of the version.
203+ */
204+ public int getBuild () {
205+ return this .build ;
206+ }
207+
208+ /**
209+ * Gets the development stage.
210+ *
211+ * @return the development stage, or null if this is a release.
212+ */
213+ @ Nullable
214+ public String getDevelopmentStage () {
215+ return this .development ;
216+ }
217+
218+ /**
219+ * Gets the snapshot version, or null if this is a release.
220+ *
221+ * @return The snapshot version.
222+ */
223+ @ Nullable
224+ public SnapshotVersion getSnapshot () {
225+ return this .snapshot ;
226+ }
227+
228+ /**
229+ * Gets if this version is a snapshot.
230+ *
231+ * @return if this version is a snapshot.
232+ */
233+ public boolean isSnapshot () {
234+ return this .snapshot != null ;
235+ }
236+
237+ /**
238+ * Gets the version String (major.minor.build) only.
239+ *
240+ * @return a normal version string.
241+ */
242+ public String getVersion () {
243+ if (getDevelopmentStage () == null ) {
244+ return String .format ("%s.%s.%s" , getMajor (), getMinor (), getBuild ());
245+ } else {
246+ return String .format ("%s.%s.%s-%s%s" , getMajor (), getMinor (), getBuild (),
247+ getDevelopmentStage (), isSnapshot () ? this .snapshot : "" );
248+ }
249+ }
250+
251+ @ Override
252+ public int compareTo (@ NotNull MinecraftVersion that ) {
253+ return COMPARATOR .compare (this , that );
254+ }
255+
256+ /**
257+ * Gets if this version was released after another version.
258+ *
259+ * @param other the other version
260+ * @return if this version was released after another version
261+ */
262+ public boolean isAfter (MinecraftVersion other ) {
263+ return compareTo (other ) > 0 ;
264+ }
265+
266+ /**
267+ * Gets if this version was released after another version, or is equal to it.
268+ *
269+ * @param other the other version
270+ * @return if this version was released after another version, or is equal to it.
271+ */
272+ public boolean isAfterOrEq (MinecraftVersion other ) {
273+ return compareTo (other ) >= 0 ;
274+ }
275+
276+ /**
277+ * Gets if this version was released before another version.
278+ *
279+ * @param other the other version
280+ * @return if this version was released before another version
281+ */
282+ public boolean isBefore (MinecraftVersion other ) {
283+ return compareTo (other ) < 0 ;
284+ }
285+
286+ /**
287+ * Gets if this version was released before another version, or is equal to it.
288+ *
289+ * @param other the other version
290+ * @return if this version was released before another version, or is equal to it.
291+ */
292+ public boolean isBeforeOrEq (MinecraftVersion other ) {
293+ return compareTo (other ) <= 0 ;
294+ }
295+
296+ /**
297+ * Gets if this version was released in the period between two other versions, or is equal
298+ * to either of them.
299+ *
300+ * @param o1 the first other version
301+ * @param o2 the second other version
302+ * @return if this version was released between the others
303+ */
304+ public boolean isBetween (MinecraftVersion o1 , MinecraftVersion o2 ) {
305+ return (isAfterOrEq (o1 ) && isBeforeOrEq (o2 )) || (isBeforeOrEq (o1 ) && isAfterOrEq (o2 ));
306+ }
307+
308+ @ Override
309+ public boolean equals (Object obj ) {
310+ if (obj == null ) return false ;
311+ if (obj == this ) return true ;
312+ if (!(obj instanceof MinecraftVersion )) return false ;
313+
314+ MinecraftVersion other = (MinecraftVersion ) obj ;
315+ return getMajor () == other .getMajor () &&
316+ getMinor () == other .getMinor () &&
317+ getBuild () == other .getBuild () &&
318+ Objects .equals (getDevelopmentStage (), other .getDevelopmentStage ());
319+ }
320+
321+ @ Override
322+ public int hashCode () {
323+ return Objects .hash (getMajor (), getMinor (), getBuild ());
324+ }
325+
326+ @ Override
327+ public String toString () {
328+ // Convert to a String that we can parse back again
329+ return String .format ("(MC: %s)" , getVersion ());
330+ }
331+ }
0 commit comments