Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Minecraft/Instance/Handler/Info/InfoMergeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,13 @@ private static McVersionType RecognizeVersionType(JsonObject versionJson, DateTi
return McVersionType.Fool;
}

if (releaseTime.Year > 2000 && releaseTime <= new DateTime(2011, 11, 16)) {
return McVersionType.Old;
if (releaseTime.Year > 2000 && releaseTime < new DateTime(2010, 12, 03)) {
return McVersionType.OldAlpha;
}

if (releaseTime > new DateTime(2010, 12, 03) && releaseTime < new DateTime(2011, 09, 19))
{
return McVersionType.OldAlpha;
}

if (versionJson.TryGetPropertyValue("type", out var typeElement)) {
Expand Down
3 changes: 2 additions & 1 deletion Minecraft/Instance/Handler/InstanceBasicHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public static McInstanceCardType RefreshInstanceCardType(IMcInstance instance) {
McVersionType.Release => McInstanceCardType.Release,
McVersionType.Snapshot => McInstanceCardType.Snapshot,
McVersionType.Fool => McInstanceCardType.Fool,
McVersionType.Old => McInstanceCardType.Old,
McVersionType.OldAlpha => McInstanceCardType.Old,
McVersionType.OldBeta => McInstanceCardType.Old,
_ => McInstanceCardType.UnknownPatchers
};
}
Expand Down
3 changes: 2 additions & 1 deletion Minecraft/Instance/Impl/PatchInstanceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public string GetLogo() {
switch (VersionType) {
case McVersionType.Fool:
return Basics.GetAppImagePath("Blocks/GoldBlock.png");
case McVersionType.Old:
case McVersionType.OldAlpha:
case McVersionType.OldBeta:
return Basics.GetAppImagePath("Blocks/CobbleStone.png");
case McVersionType.Snapshot:
return Basics.GetAppImagePath("Blocks/CommandBlock.png");
Expand Down
3 changes: 2 additions & 1 deletion Minecraft/Launch/Services/PreLaunchService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ private string DetermineRequiredLanguage(string currentLang) {
var mcVersionMinor = instance.InstanceInfo.McVersionMinor;
var mcReleaseDate = instance.InstanceInfo.McReleaseDate;
var isUnder11 = mcReleaseDate < new DateTime(2012, 1, 12)
|| instance.InstanceInfo.VersionType == McVersionType.Old
|| instance.InstanceInfo.VersionType == McVersionType.OldAlpha
|| instance.InstanceInfo.VersionType == McVersionType.OldBeta
|| (mcVersionMinor == 1 && instance.InstanceInfo.McVersionBuild < 1);

// For 1.0 and lower version, return "none" as no language option is available
Expand Down
189 changes: 189 additions & 0 deletions Minecraft/McVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using PCL.Core.App;

namespace PCL.Core.Minecraft;

public enum McVersionType
{
Snapshot,
Release,
Fool,
OldAlpha,
OldBeta,
Unknown
}

[LifecycleService(LifecycleState.Loaded, Priority = 100)]
[LifecycleScope("mc_version", "MC 版本")]
public sealed partial class McVersion
{
#pragma warning disable CS8618
// Expected CS8618
// Initialized when loaded
public static JsonNode Manifest { get; set; }
#pragma warning restore

[LifecycleStart]
private static async Task _Start()
{
Manifest = await RefreshManifest();
}

[LifecycleStop]
private static void _Stop() { }

public McVersion(string id)
{
Id = id;
JsonUrl = "";
var manifest = Manifest;
if (manifest["versions"] is not null)
{
var versions = manifest["versions"] as JsonArray ?? [];
var current = versions.OfType<JsonNode>().FirstOrDefault(version => (version["id"]?.ToString() ?? "") == id);

if (current is null)
throw new FormatException();

JsonUrl = current["url"]?.ToString() ?? "";

var time = current["time"]?.GetValue<DateTime>().ToUniversalTime();
var releaseTime = current["releaseTime"]?.GetValue<DateTime>().ToUniversalTime();

if (time is null || releaseTime is null)
throw new FormatException();

Time = (DateTime)time;
ReleaseTime = (DateTime)releaseTime;

switch (current["type"]?.ToString() ?? "")
{
case "snapshot":
VersionType = McVersionType.Snapshot;
var idLower = id.ToLower();
if (idLower.StartsWith("1.")
&& !idLower.Contains("rc")
&& !idLower.Contains("combat")
&& !idLower.Contains("experimental")
&& !idLower.Contains("pre"))
VersionType = McVersionType.Release;
switch (idLower)
{
case "20w14infinite":
case "20w14∞":
Id = "20w14∞";
VersionType = McVersionType.Fool;
break;
case "3d shareware v1.34":
case "1.rv-pre1":
case "15w14a":
case "2.0":
case "22w13oneblockatatime":
case "23w13a_or_b":
case "24w14potato":
VersionType = McVersionType.Fool;
break;
default:
var releaseDate = current["releaseTime"]?.GetValue<DateTime>().ToUniversalTime()
.AddHours(2);
if (releaseDate is { Month: 4, Day: 1 })
{
VersionType = McVersionType.Fool;
break;
}

VersionType = McVersionType.Snapshot;
break;
}

break;
case "release":
VersionType = McVersionType.Release;
break;
case "old_alpha":
VersionType = McVersionType.OldAlpha;
break;
case "old_beta":
VersionType = McVersionType.OldBeta;
break;
default:
VersionType = McVersionType.Unknown;
break;
}

}
else
throw new FormatException();

return;
}
public string Id { get; }
public McVersionType VersionType { get; }
public string JsonUrl { get; }
public DateTime Time { get; }
public DateTime ReleaseTime { get; }

public static bool operator <(McVersion first, McVersion second)
{
return first.ReleaseTime < second.ReleaseTime;
}
public static bool operator <=(McVersion first, McVersion second)
{
return first.ReleaseTime <= second.ReleaseTime;
}

public static bool operator >(McVersion first, McVersion second)
{
return first.ReleaseTime > second.ReleaseTime;
}
public static bool operator >=(McVersion first, McVersion second)
{
return first.ReleaseTime >= second.ReleaseTime;
}

public static bool operator ==(McVersion first, McVersion second)
{
return first.Id == second.Id;
}
public static bool operator !=(McVersion first, McVersion second)
{
return first.Id != second.Id;
}

public async static Task<JsonNode> RefreshManifest()
{
var client = new HttpClient();
var getTask = await client.GetAsync("https://piston-meta.mojang.com/mc/game/version_manifest.json");
var readTask = await getTask.Content.ReadAsStringAsync();
return Manifest = JsonNode.Parse(readTask) ?? new JsonObject();
}

public override bool Equals(object? obj)
{
if (ReferenceEquals(this, obj))
return true;

if (ReferenceEquals(obj, null))
return false;

try
{
var converted = (McVersion)obj;
return Id == converted.Id;
}
catch (InvalidCastException)
{
return false;
}
}

public override int GetHashCode()
{
return HashCode.Combine(Id, (int)VersionType);
}
}

86 changes: 86 additions & 0 deletions Minecraft/McVersionRange.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace PCL.Core.Minecraft;

/// <summary>
/// 版本区间(左闭右开区间)
/// </summary>
public class McVersionRange(McVersion start, McVersion end)
{
public McVersion Start { get; } = start;
public McVersion End { get; } = end;

public virtual bool Validate(McVersion version)
=> version.ReleaseTime >= Start.ReleaseTime && version.ReleaseTime < End.ReleaseTime;

public override string ToString()
{
return $"{Start.Id}..{End.Id}";
}
}

/// <summary>
/// 版本区间(闭区间)
/// </summary>
public class McVersionRangeClosed(McVersion start, McVersion end) : McVersionRange(start, end)
{
public override bool Validate(McVersion version)
=> version.ReleaseTime >= Start.ReleaseTime && version.ReleaseTime <= End.ReleaseTime;

public override string ToString()
{
if (Start.Id == End.Id)
return $"{Start.Id}";
return $"{Start.Id}..={End.Id}";
}
}


public class McVersionRanges(IList<McVersionRange> ranges)
{
public List<McVersionRange> RangeList { get; set; } = [..ranges];

public bool Validate(McVersion version)
=> RangeList.Any(range => range.Validate(version));

/// <summary>
/// 解析文本形式的版本区间。
/// </summary>
/// <param name="versionRange">
/// 代表版本范围的字符串。<br/>
/// 使用规范(多个范围以半角逗号分隔):<br/>
/// "A..B" => [A, B) <br/>
/// "A..=B" => [A, B] <br/>
/// "A" => {A} <br/>
/// </param>
/// <returns></returns>
public static McVersionRanges Parse(string versionRange)
{
IList<McVersionRange> ranges = [];
versionRange = versionRange.Replace(" ", "");
foreach (var str in versionRange.Split(","))
{
if (str.Contains("..="))
ranges.Add(new McVersionRangeClosed(
new McVersion(str.Split("..=")[0]),
new McVersion(str.Split("..=")[1])
));
else if (str.Contains(".."))
ranges.Add(new McVersionRange(
new McVersion(str.Split("..=")[0]),
new McVersion(str.Split("..=")[1])
));
else
ranges.Add(new McVersionRangeClosed(new McVersion(str), new McVersion(str)));
}
return new McVersionRanges(ranges);
}

public override string ToString()
{
var str = RangeList.Aggregate("", (current, range) => current + $", {range}");
return str[2..];
}
}
18 changes: 18 additions & 0 deletions Minecraft/ModLoader/ModLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json.Nodes;

namespace PCL.Core.Minecraft.ModLoader;

public abstract class ModLoader
{
public abstract McVersionRanges VersionRanges { get; }
public abstract string Name { get; }
public abstract string Version { get; }
public abstract string Icon { get; }

public virtual void MergeJson(ref JsonNode node) { }
public virtual void MergeVersionName(ref string name) { }

public virtual void PreInstallTask() { }
public virtual void PostInstallTask() { }
public virtual void UninstallTask(ref JsonNode node, ref string name) { }
}
Loading