2021-08-06 08:25:46 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2022-10-02 18:31:08 +08:00
|
|
|
|
using System.Net.Http;
|
2026-04-04 11:17:44 +02:00
|
|
|
|
using System.Threading;
|
2022-10-02 18:31:08 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2021-08-06 08:25:46 +02:00
|
|
|
|
|
|
|
|
|
|
/// !!! ATTENTION !!!
|
|
|
|
|
|
/// By using these functions you agree to the ToS of the Mojang API.
|
|
|
|
|
|
/// You should note that all public APIs are rate limited so you are expected to cache the results.
|
|
|
|
|
|
/// This is currently set at 600 requests per 10 minutes but this may change.
|
2021-08-13 08:27:53 +02:00
|
|
|
|
/// Source: https://wiki.vg/Mojang_API
|
2021-08-06 08:25:46 +02:00
|
|
|
|
/// !!! ATTENTION !!!
|
|
|
|
|
|
|
|
|
|
|
|
namespace MinecraftClient.Protocol
|
|
|
|
|
|
{
|
2021-08-13 08:27:53 +02:00
|
|
|
|
// Enum to display the status of different services
|
|
|
|
|
|
public enum ServiceStatus { red, yellow, green };
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Information about a players Skin.
|
|
|
|
|
|
/// Empty string if not available.
|
|
|
|
|
|
/// </summary>
|
Modernize data carriers to records and add primary constructors
Convert 14 data carrier classes to records:
- VillagerInfo, MapIcon, EnchantmentData: non-positional records (mutable properties)
- ForgeMod, SkinInfo, VillagerTrade, Node, Response: positional records
- CommandNode: sealed positional record
- CommandArgumentDescriptor: readonly record struct
- ColorRGBA: record struct (multiple constructors preserved)
- RecipeConfig, Recipe, BannerLayer: non-positional records
Add primary constructors to 6 classes:
- DataTypes, Protocol18Terrain, Protocol18Forge, ItemMovingHelper,
LastSeenMessageList, Acknowledgment, SuggestionTooltip
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 01:31:17 +00:00
|
|
|
|
public record SkinInfo(string SkinUrl = "", string CapeUrl = "", string SkinModel = "");
|
2021-08-13 08:27:53 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Status of the single Mojang services
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class MojangServiceStatus
|
|
|
|
|
|
{
|
|
|
|
|
|
public readonly ServiceStatus MinecraftNet;
|
|
|
|
|
|
public readonly ServiceStatus SessionMinecraftNet;
|
|
|
|
|
|
public readonly ServiceStatus AccountMojangCom;
|
|
|
|
|
|
public readonly ServiceStatus AuthserverMojangCom;
|
|
|
|
|
|
public readonly ServiceStatus SessionserverMojangCom;
|
|
|
|
|
|
public readonly ServiceStatus ApiMojangCom;
|
|
|
|
|
|
public readonly ServiceStatus TexturesMinecraftNet;
|
|
|
|
|
|
public readonly ServiceStatus MojangCom;
|
|
|
|
|
|
|
|
|
|
|
|
public MojangServiceStatus(ServiceStatus minecraftNet = ServiceStatus.red,
|
|
|
|
|
|
ServiceStatus sessionMinecraftNet = ServiceStatus.red,
|
|
|
|
|
|
ServiceStatus accountMojangCom = ServiceStatus.red,
|
|
|
|
|
|
ServiceStatus authserverMojangCom = ServiceStatus.red,
|
|
|
|
|
|
ServiceStatus sessionserverMojangCom = ServiceStatus.red,
|
|
|
|
|
|
ServiceStatus apiMojangCom = ServiceStatus.red,
|
|
|
|
|
|
ServiceStatus texturesMinecraftNet = ServiceStatus.red,
|
|
|
|
|
|
ServiceStatus mojangCom = ServiceStatus.red)
|
|
|
|
|
|
{
|
|
|
|
|
|
MinecraftNet = minecraftNet;
|
|
|
|
|
|
SessionMinecraftNet = sessionMinecraftNet;
|
|
|
|
|
|
AccountMojangCom = accountMojangCom;
|
|
|
|
|
|
AuthserverMojangCom = authserverMojangCom;
|
|
|
|
|
|
SessionserverMojangCom = sessionserverMojangCom;
|
|
|
|
|
|
ApiMojangCom = apiMojangCom;
|
|
|
|
|
|
TexturesMinecraftNet = texturesMinecraftNet;
|
|
|
|
|
|
MojangCom = mojangCom;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-06 08:25:46 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Provides methods to easily interact with the Mojang API.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class MojangAPI
|
|
|
|
|
|
{
|
|
|
|
|
|
// Initialize webclient for all functions
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private static readonly HttpClient httpClient = new();
|
2021-08-06 08:25:46 +02:00
|
|
|
|
|
|
|
|
|
|
// Can be removed in newer C# versions.
|
|
|
|
|
|
// Replace with DateTimeOffset.FromUnixTimeMilliseconds()
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Converts a Unix time to a normal Datetime
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="unixTimeStamp">A unix timestamp as double</param>
|
|
|
|
|
|
/// <returns>Datetime of unix timestamp</returns>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private static DateTime UnixTimeStampToDateTime(double unixTimeStamp)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
// Unix timestamp is seconds past epoch
|
2022-10-02 18:31:08 +08:00
|
|
|
|
DateTime dateTime = new(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
2021-08-06 08:25:46 +02:00
|
|
|
|
dateTime = dateTime.AddMilliseconds(unixTimeStamp).ToLocalTime();
|
|
|
|
|
|
return dateTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Can be removed in newer C# versions.
|
|
|
|
|
|
|
2021-08-13 08:27:53 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Converts a string to a ServiceStatus enum.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="s">string to convert</param>
|
|
|
|
|
|
/// <returns>ServiceStatus enum, red as default.</returns>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private static ServiceStatus StringToServiceStatus(string s)
|
2021-08-13 08:27:53 +02:00
|
|
|
|
{
|
|
|
|
|
|
|
2022-10-02 18:31:08 +08:00
|
|
|
|
if (Enum.TryParse(s, out ServiceStatus servStat))
|
2021-08-13 08:27:53 +02:00
|
|
|
|
{
|
|
|
|
|
|
return servStat;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// return red as standard value.
|
|
|
|
|
|
return ServiceStatus.red;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-06 08:25:46 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Obtain the UUID of a Player through its name
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="name">Playername</param>
|
|
|
|
|
|
/// <returns>UUID as string</returns>
|
2026-04-04 11:17:44 +02:00
|
|
|
|
public static string NameToUuid(string name) =>
|
|
|
|
|
|
NameToUuidAsync(name).GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task<string> NameToUuidAsync(string name, CancellationToken cancellationToken = default)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-04-04 11:17:44 +02:00
|
|
|
|
string responseBody = await httpClient.GetStringAsync("https://api.mojang.com/users/profiles/minecraft/" + name, cancellationToken);
|
|
|
|
|
|
string result = Json.ParseJson(responseBody)!["id"]!.GetStringValue();
|
2022-10-02 18:31:08 +08:00
|
|
|
|
return result;
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception) { return string.Empty; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Obtain the Name of a player through its UUID
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="uuid">UUID of a player</param>
|
|
|
|
|
|
/// <returns>Players UUID</returns>
|
2026-04-04 11:17:44 +02:00
|
|
|
|
public static string UuidToCurrentName(string uuid) =>
|
|
|
|
|
|
UuidToCurrentNameAsync(uuid).GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task<string> UuidToCurrentNameAsync(string uuid, CancellationToken cancellationToken = default)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
// Perform web request
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-04-04 11:17:44 +02:00
|
|
|
|
string responseBody = await httpClient.GetStringAsync("https://api.mojang.com/user/profiles/" + uuid + "/names", cancellationToken);
|
|
|
|
|
|
var nameChanges = Json.ParseJson(responseBody)!.AsArray();
|
2021-08-06 08:25:46 +02:00
|
|
|
|
|
|
|
|
|
|
// Names are sorted from past to most recent. We need to get the last name in the list
|
2026-03-22 16:39:26 +00:00
|
|
|
|
return nameChanges[^1]!["name"]!.GetStringValue();
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception) { return string.Empty; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get the name history from a UUID
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="uuid">UUID of a player</param>
|
|
|
|
|
|
/// <returns>Name history, as a dictionary</returns>
|
2026-04-04 11:17:44 +02:00
|
|
|
|
public static Dictionary<string, DateTime> UuidToNameHistory(string uuid) =>
|
|
|
|
|
|
UuidToNameHistoryAsync(uuid).GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task<Dictionary<string, DateTime>> UuidToNameHistoryAsync(string uuid, CancellationToken cancellationToken = default)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
Dictionary<string, DateTime> tempDict = new();
|
2026-03-22 16:39:26 +00:00
|
|
|
|
System.Text.Json.Nodes.JsonArray jsonDataList;
|
2021-08-06 08:25:46 +02:00
|
|
|
|
|
|
|
|
|
|
// Perform web request
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-04-04 11:17:44 +02:00
|
|
|
|
string responseBody = await httpClient.GetStringAsync("https://api.mojang.com/user/profiles/" + uuid + "/names", cancellationToken);
|
|
|
|
|
|
jsonDataList = Json.ParseJson(responseBody)!.AsArray();
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception) { return tempDict; }
|
|
|
|
|
|
|
2026-03-22 16:39:26 +00:00
|
|
|
|
foreach (var jsonData in jsonDataList)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
2026-03-22 16:39:26 +00:00
|
|
|
|
var obj = jsonData!.AsObject();
|
|
|
|
|
|
if (obj.Count > 1)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
2026-03-22 16:39:26 +00:00
|
|
|
|
DateTimeOffset creationDate = UnixTimeStampToDateTime(Convert.ToDouble(jsonData["changedToAt"].GetStringValue()));
|
2021-08-06 08:25:46 +02:00
|
|
|
|
|
2026-03-22 16:39:26 +00:00
|
|
|
|
tempDict.Add(jsonData["name"]!.GetStringValue(), creationDate.DateTime);
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
// The first entry does not contain a change date.
|
2026-03-22 16:39:26 +00:00
|
|
|
|
else if (obj.Count > 0)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
// Add an undefined time to it.
|
2026-03-22 16:39:26 +00:00
|
|
|
|
tempDict.Add(jsonData["name"]!.GetStringValue(), new DateTime());
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return tempDict;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get the Mojang API status
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>Dictionary of the Mojang services</returns>
|
2026-04-04 11:17:44 +02:00
|
|
|
|
public static MojangServiceStatus GetMojangServiceStatus() =>
|
|
|
|
|
|
GetMojangServiceStatusAsync().GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task<MojangServiceStatus> GetMojangServiceStatusAsync(CancellationToken cancellationToken = default)
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
2026-03-22 16:39:26 +00:00
|
|
|
|
System.Text.Json.Nodes.JsonArray jsonDataList;
|
2021-08-06 08:25:46 +02:00
|
|
|
|
|
|
|
|
|
|
// Perform web request
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-04-04 11:17:44 +02:00
|
|
|
|
string responseBody = await httpClient.GetStringAsync("https://status.mojang.com/check", cancellationToken);
|
|
|
|
|
|
jsonDataList = Json.ParseJson(responseBody)!.AsArray();
|
2022-10-02 18:31:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception)
|
2022-12-06 15:50:17 +08:00
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
return new MojangServiceStatus();
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-13 08:27:53 +02:00
|
|
|
|
// Convert string to enum values and store them inside a MojangeServiceStatus object.
|
2026-03-22 16:39:26 +00:00
|
|
|
|
return new MojangServiceStatus(minecraftNet: StringToServiceStatus(jsonDataList[0]!["minecraft.net"]!.GetStringValue()),
|
|
|
|
|
|
sessionMinecraftNet: StringToServiceStatus(jsonDataList[1]!["session.minecraft.net"]!.GetStringValue()),
|
|
|
|
|
|
accountMojangCom: StringToServiceStatus(jsonDataList[2]!["account.mojang.com"]!.GetStringValue()),
|
|
|
|
|
|
authserverMojangCom: StringToServiceStatus(jsonDataList[3]!["authserver.mojang.com"]!.GetStringValue()),
|
|
|
|
|
|
sessionserverMojangCom: StringToServiceStatus(jsonDataList[4]!["sessionserver.mojang.com"]!.GetStringValue()),
|
|
|
|
|
|
apiMojangCom: StringToServiceStatus(jsonDataList[5]!["api.mojang.com"]!.GetStringValue()),
|
|
|
|
|
|
texturesMinecraftNet: StringToServiceStatus(jsonDataList[6]!["textures.minecraft.net"]!.GetStringValue()),
|
|
|
|
|
|
mojangCom: StringToServiceStatus(jsonDataList[7]!["mojang.com"]!.GetStringValue())
|
2021-08-13 08:27:53 +02:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Obtain links to skin, skinmodel and cape of a player.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param uuid="uuid">UUID of a player</param>
|
|
|
|
|
|
/// <returns>Dictionary with a link to the skin and cape of a player.</returns>
|
2026-04-04 11:17:44 +02:00
|
|
|
|
public static SkinInfo GetSkinInfo(string uuid) =>
|
|
|
|
|
|
GetSkinInfoAsync(uuid).GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task<SkinInfo> GetSkinInfoAsync(string uuid, CancellationToken cancellationToken = default)
|
2021-08-13 08:27:53 +02:00
|
|
|
|
{
|
2026-03-22 16:39:26 +00:00
|
|
|
|
System.Text.Json.Nodes.JsonObject textureObj;
|
2021-08-13 08:27:53 +02:00
|
|
|
|
string base64SkinInfo;
|
2026-03-22 16:39:26 +00:00
|
|
|
|
System.Text.Json.Nodes.JsonNode? decodedJsonSkinInfo;
|
2021-08-13 08:27:53 +02:00
|
|
|
|
|
|
|
|
|
|
// Perform web request
|
|
|
|
|
|
try
|
2021-08-06 08:25:46 +02:00
|
|
|
|
{
|
2026-04-04 11:17:44 +02:00
|
|
|
|
string responseBody = await httpClient.GetStringAsync("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid, cancellationToken);
|
2021-08-13 08:27:53 +02:00
|
|
|
|
// Obtain the Base64 encoded skin information from the API. Discard the rest, since it can be obtained easier through other requests.
|
2026-04-04 11:17:44 +02:00
|
|
|
|
base64SkinInfo = Json.ParseJson(responseBody)!["properties"]![0]!["value"]!.GetStringValue();
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
2021-08-13 08:27:53 +02:00
|
|
|
|
catch (Exception) { return new SkinInfo(); }
|
2021-08-06 08:25:46 +02:00
|
|
|
|
|
2021-08-13 08:27:53 +02:00
|
|
|
|
// Parse the decoded string to the JSON format.
|
|
|
|
|
|
decodedJsonSkinInfo = Json.ParseJson(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(base64SkinInfo)));
|
|
|
|
|
|
|
|
|
|
|
|
// Assert temporary variable for readablity.
|
|
|
|
|
|
// Contains skin and cape information.
|
2026-03-22 16:39:26 +00:00
|
|
|
|
textureObj = decodedJsonSkinInfo!["textures"]!.AsObject();
|
2021-08-13 08:27:53 +02:00
|
|
|
|
|
|
|
|
|
|
// Can apparently be missing, if no custom skin is set.
|
2026-03-22 16:39:26 +00:00
|
|
|
|
if (textureObj.ContainsKey("SKIN"))
|
2021-08-13 08:27:53 +02:00
|
|
|
|
{
|
Modernize data carriers to records and add primary constructors
Convert 14 data carrier classes to records:
- VillagerInfo, MapIcon, EnchantmentData: non-positional records (mutable properties)
- ForgeMod, SkinInfo, VillagerTrade, Node, Response: positional records
- CommandNode: sealed positional record
- CommandArgumentDescriptor: readonly record struct
- ColorRGBA: record struct (multiple constructors preserved)
- RecipeConfig, Recipe, BannerLayer: non-positional records
Add primary constructors to 6 classes:
- DataTypes, Protocol18Terrain, Protocol18Forge, ItemMovingHelper,
LastSeenMessageList, Acknowledgment, SuggestionTooltip
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 01:31:17 +00:00
|
|
|
|
return new SkinInfo(SkinUrl: textureObj["SKIN"]!["url"] is not null ? textureObj["SKIN"]!["url"]!.GetStringValue() : string.Empty,
|
|
|
|
|
|
CapeUrl: textureObj.ContainsKey("CAPE") ? textureObj["CAPE"]!["url"]!.GetStringValue() : string.Empty,
|
|
|
|
|
|
SkinModel: textureObj["SKIN"]!["metadata"] is not null ? "Alex" : "Steve");
|
2021-08-13 08:27:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
Modernize data carriers to records and add primary constructors
Convert 14 data carrier classes to records:
- VillagerInfo, MapIcon, EnchantmentData: non-positional records (mutable properties)
- ForgeMod, SkinInfo, VillagerTrade, Node, Response: positional records
- CommandNode: sealed positional record
- CommandArgumentDescriptor: readonly record struct
- ColorRGBA: record struct (multiple constructors preserved)
- RecipeConfig, Recipe, BannerLayer: non-positional records
Add primary constructors to 6 classes:
- DataTypes, Protocol18Terrain, Protocol18Forge, ItemMovingHelper,
LastSeenMessageList, Acknowledgment, SuggestionTooltip
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 01:31:17 +00:00
|
|
|
|
return new SkinInfo(CapeUrl: textureObj.ContainsKey("CAPE") ? textureObj["CAPE"]!["url"]!.GetStringValue() : string.Empty,
|
|
|
|
|
|
SkinModel: DefaultModelAlex(uuid) ? "Alex" : "Steve");
|
2021-08-13 08:27:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the playermodel that is assigned to the account by default.
|
|
|
|
|
|
/// (Before the skin is changed for the first time.)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="uuid">UUID of a Player</param>
|
|
|
|
|
|
/// <returns>True if the default model for this UUID is Alex</returns>
|
|
|
|
|
|
public static bool DefaultModelAlex(string uuid)
|
|
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
return HashCode(uuid) % 2 == 1;
|
2021-08-13 08:27:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Creates the hash of an UUID
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hash">UUID of a player.</param>
|
|
|
|
|
|
/// <returns></returns>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private static int HashCode(string hash)
|
2021-08-13 08:27:53 +02:00
|
|
|
|
{
|
|
|
|
|
|
byte[] data = GuidExtensions.ToLittleEndian(new Guid(hash)).ToByteArray();
|
|
|
|
|
|
|
|
|
|
|
|
ulong msb = 0;
|
|
|
|
|
|
ulong lsb = 0;
|
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
|
|
msb = (msb << 8) | (uint)(data[i] & 0xff);
|
|
|
|
|
|
for (int i = 8; i < 16; i++)
|
|
|
|
|
|
lsb = (lsb << 8) | (uint)(data[i] & 0xff);
|
|
|
|
|
|
var hilo = msb ^ lsb;
|
|
|
|
|
|
|
|
|
|
|
|
return ((int)(hilo >> 32)) ^ (int)hilo;
|
2021-08-06 08:25:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|