Skip to content

Commit 0391d10

Browse files
committed
feather diff viewer
1 parent b315d32 commit 0391d10

8 files changed

Lines changed: 315 additions & 3 deletions

File tree

public/develop.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,6 @@ import {
173173

174174
possibleVersions = minecraftStableVersions;
175175
updateVersionList()
176-
updateOrnitheDependencies()
176+
await updateOrnitheDependencies()
177177

178178
})()

public/feather_diff.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// diffMappings, printDiff, diffMemberArray from https://github.com/modmuss50/YarnDiff/tree/master
2+
3+
import {
4+
getFeatherBuildMaven,
5+
getFeatherVersionMeta,
6+
getMinecraftStableVersions,
7+
getMinecraftVersions,
8+
} from "./meta_maven_utils.js";
9+
10+
import { decompressSync } from "https://cdn.skypack.dev/fflate@0.8.2?min";
11+
import * as tiny from "./tiny_mappings.js";
12+
13+
(async () => {
14+
const minecraftStableVersions = await getMinecraftStableVersions();
15+
const minecraftAllVersions = await getMinecraftVersions();
16+
17+
let possibleVersions;
18+
19+
const versionSelectorInput = document.getElementById("mc-version");
20+
const versionListElement = document.getElementById("version-list");
21+
const allowSnapshotsCheck = document.getElementById("allow-snapshots");
22+
23+
const buildSourceElement = document.getElementById("build-source");
24+
const buildTargetElement = document.getElementById("build-target");
25+
const diffViewerElement = document.getElementById("diff-viewer");
26+
27+
async function updateFeatherBuilds() {
28+
if (
29+
possibleVersions.some((version) => versionSelectorInput.value === version)
30+
) {
31+
await getFeatherVersionMeta(versionSelectorInput.value).then(
32+
(featherVersionMeta) => {
33+
buildSourceElement.innerHTML = "";
34+
buildTargetElement.innerHTML = "";
35+
for (const featherVersion of featherVersionMeta) {
36+
const featherVersionElement = document.createElement("option");
37+
featherVersionElement.innerText = "Build " + featherVersion.build;
38+
featherVersionElement.value = featherVersion.version;
39+
buildSourceElement.appendChild(featherVersionElement);
40+
buildTargetElement.appendChild(
41+
featherVersionElement.cloneNode(true),
42+
);
43+
}
44+
},
45+
);
46+
47+
// Hide the diff viewer bc the source and target builds are the same
48+
diffViewerElement.style.display = "none";
49+
}
50+
}
51+
52+
async function getTinyMappings(version) {
53+
let arrayBuf = await getFeatherBuildMaven(version)
54+
.then((response) => response.blob()) // Get response as a Blob
55+
.then(async (blob) => {
56+
const arrayBuffer = await blob.arrayBuffer(); // Convert Blob to ArrayBuffer
57+
// Convert to Uint8Array
58+
return new Uint8Array(arrayBuffer);
59+
});
60+
const decompressed = decompressSync(arrayBuf);
61+
const decoder = new TextDecoder("utf-8");
62+
const file = decoder.decode(decompressed);
63+
return tiny.parseTiny(file);
64+
}
65+
66+
async function updateFeatherDiff() {
67+
const source = buildSourceElement.value;
68+
const target = buildTargetElement.value;
69+
70+
if (source === target) {
71+
console.log("Source and target builds are the same");
72+
// Hide the diff viewer
73+
diffViewerElement.style.display = "none";
74+
return;
75+
}
76+
// Display the diff viewer
77+
diffViewerElement.style.display = "inline";
78+
79+
const sourceMappings = await getTinyMappings(source);
80+
const targetMappings = await getTinyMappings(target);
81+
82+
diffMappings(sourceMappings, targetMappings);
83+
}
84+
85+
function diffMappings(source, target) {
86+
printDiff(diffMemberArray(source.classes, target), "classes-diff")
87+
printDiff(diffMemberArray(source.methods, target), "methods-diff")
88+
printDiff(diffMemberArray(source.fields, target), "fields-diff")
89+
}
90+
91+
function printDiff(diff, elementID) {
92+
document.getElementById(elementID).innerText = diff.map(value => `${value.source} -> ${value.target}`).join("\n")
93+
}
94+
95+
function diffMemberArray(source, targetMappings) {
96+
let diff = []
97+
98+
source.forEach(source => {
99+
let target = targetMappings.find(source.calamus)
100+
101+
if (target !== undefined && source.feather !== target.feather) {
102+
diff.push({
103+
source: source.feather,
104+
target: target.feather
105+
})
106+
}
107+
})
108+
return diff
109+
}
110+
111+
versionSelectorInput.addEventListener(
112+
"input",
113+
async (_) => await updateFeatherBuilds(),
114+
);
115+
116+
allowSnapshotsCheck.addEventListener("change", (_) => {
117+
if (allowSnapshotsCheck.checked) {
118+
possibleVersions = minecraftAllVersions;
119+
} else {
120+
possibleVersions = minecraftStableVersions;
121+
}
122+
updateVersionList();
123+
});
124+
125+
buildSourceElement.addEventListener(
126+
"change",
127+
async (_) => await updateFeatherDiff(),
128+
);
129+
130+
buildTargetElement.addEventListener(
131+
"change",
132+
async (_) => await updateFeatherDiff(),
133+
);
134+
135+
function updateVersionList() {
136+
const list = possibleVersions;
137+
while (versionListElement.firstChild)
138+
versionListElement.removeChild(versionListElement.lastChild);
139+
list.forEach((e) => {
140+
const opt = new Option();
141+
opt.value = e;
142+
versionListElement.appendChild(opt);
143+
});
144+
}
145+
146+
possibleVersions = minecraftStableVersions;
147+
updateVersionList();
148+
await updateFeatherBuilds();
149+
})();

public/meta_maven_utils.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ async function getMinecraftVersionsMeta() {
2020
return await getFromMeta("versions", "game");
2121
}
2222

23-
async function getFeatherVersionMeta(mcVersion) {
23+
export async function getFeatherVersionMeta(mcVersion) {
2424
return await getFromMeta("versions", "feather", mcVersion);
2525
}
2626

@@ -113,4 +113,24 @@ export async function getLatestOsl() {
113113
.then(l => l.sort((e1, e2) => compareVersion(e1.version, e2.version)))
114114
.then(([head, ..._]) => head)
115115
.then(e => e.version);
116+
}
117+
118+
const MAVEN = "maven." + URL;
119+
120+
function makeMavenUrl(...pathComponents) {
121+
let url = MAVEN + "/releases";
122+
for (const pathComponent of pathComponents) {
123+
url += "/";
124+
url += pathComponent;
125+
}
126+
return "https://" + url;
127+
}
128+
129+
function makeOrnitheMavenUrl(...pathComponents) {
130+
return makeMavenUrl("net/ornithemc", ...pathComponents);
131+
}
132+
133+
export async function getFeatherBuildMaven(version) {
134+
const url = makeOrnitheMavenUrl("feather", version, "feather-" + version + "-tiny.gz");
135+
return await fetch(url);
116136
}

public/tiny_mappings.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Adapted from https://github.com/modmuss50/YarnDiff/tree/master
2+
//
3+
4+
'use strict';
5+
6+
export class Mapping {
7+
calamus
8+
feather
9+
10+
constructor(calamus, feather) {
11+
this.calamus = calamus;
12+
this.feather = feather;
13+
}
14+
}
15+
16+
export class Mappings {
17+
classes
18+
fields
19+
methods
20+
21+
constructor() {
22+
this.classes = new Map();
23+
this.fields = new Map();
24+
this.methods = new Map();
25+
}
26+
27+
find(name) {
28+
let type = name.split("_")[0].split("/").pop()
29+
switch (type) {
30+
case "c":
31+
return this.classes.get(name)
32+
case "m":
33+
return this.methods.get(name)
34+
case "f":
35+
return this.fields.get(name)
36+
default:
37+
//throw `Unsupported intermediary type ${type} for input ${name}`;
38+
}
39+
}
40+
41+
add(nameable, mapping) {
42+
nameable.set(mapping.calamus, mapping);
43+
}
44+
}
45+
46+
export function parseTiny(input) {
47+
const mappings = new Mappings()
48+
49+
let foundHeader = false;
50+
let namespace = {};
51+
52+
input.split("\n").map(function (value) {
53+
return value.split("\t")
54+
}).forEach(function (split) {
55+
56+
//Reads the header to find the coloum of the mapping format
57+
if (!foundHeader) {
58+
if (split[0] !== 'v1') {
59+
throw "Unsupported mapping format"
60+
}
61+
foundHeader = true;
62+
for (let i = 1; i < split.length; i++) {
63+
namespace[split[i]] = i - 1
64+
}
65+
return
66+
}
67+
68+
switch (split[0]) {
69+
case "CLASS":
70+
mappings.add(mappings.classes, new Mapping(split[namespace.intermediary + 1], split[namespace.named + 1]))
71+
break
72+
case "FIELD":
73+
mappings.add(mappings.fields, new Mapping(split[namespace.intermediary + 3], split[namespace.named + 3]))
74+
break
75+
case "METHOD":
76+
mappings.add(mappings.methods, new Mapping(split[namespace.intermediary + 3], split[namespace.named + 3]))
77+
break
78+
default:
79+
//Nope
80+
}
81+
})
82+
83+
console.log(`Loaded ${mappings.classes.size} classes, ${mappings.fields.size} fields, ${mappings.methods.size} methods`)
84+
return mappings
85+
}

src/layouts/Layout.astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const { title } = Astro.props;
3434
&bull;
3535
<a class="w-fit" href="/develop">Develop</a>
3636
&bull;
37+
<a class="w-fit" href="/featherDiff">Feather diff</a>
38+
&bull;
3739
<a class="w-fit" href="https://discord.gg/JbRbRf62pn">Discord</a>
3840
&bull;
3941
<a class="w-fit" href="https://github.com/OrnitheMC">Github</a>

src/pages/develop.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import Layout from "../layouts/Layout.astro";
3232
<label for="mod-loader-quilt">Quilt</label>
3333
</fieldset>
3434

35+
<label for="mc-version">Game version</label>
3536
<input type="text" id="mc-version" list="version-list" value="1.7.2" />
36-
<label for="mc-version-autocomplete">Game version</label>
3737
<datalist id="version-list">
3838
<option value="1.7.2"></option>
3939
<!-- DO NOT INSERT ANYTHING HERE -->

src/pages/featherDiff.astro

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
import Layout from "../layouts/Layout.astro";
3+
---
4+
5+
<script type="module" src="/feather_diff.js" defer></script>
6+
7+
<Layout title="Feather - Build diff viewer">
8+
<main class="flex flex-col gap-4">
9+
<!-- Familiar, eh? -wwp -->
10+
<h1>Feather - Build diff viewer</h1>
11+
12+
<h2>Latest versions</h2>
13+
<div class="flex flex-col gap-2 items-start">
14+
<fieldset>
15+
<input type="checkbox" id="allow-snapshots" />
16+
<label for="allow-snapshots">Include Snapshots</label>
17+
</fieldset>
18+
19+
<label for="mc-version">Game version</label>
20+
<input type="text" id="mc-version" list="version-list" value="1.7.2" />
21+
<datalist id="version-list">
22+
<option value="1.7.2"></option>
23+
<!-- DO NOT INSERT ANYTHING HERE -->
24+
<!-- Data here will be inserted by JavaScript -->
25+
</datalist>
26+
27+
<h2>Builds:</h2>
28+
<label for="build-source">Source build</label>
29+
<select id="build-source">
30+
<!-- DO NOT INSERT ANYTHING HERE -->
31+
<!-- Data here will be inserted by JavaScript -->
32+
</select>
33+
<label for="build-target">Target build</label>
34+
<select id="build-target">
35+
<!-- DO NOT INSERT ANYTHING HERE -->
36+
<!-- Data here will be inserted by JavaScript -->
37+
</select>
38+
39+
<div id="diff-viewer" style="display:none">
40+
<h3>Classes:</h3>
41+
<p id="classes-diff"></p>
42+
<h3>Methods:</h3>
43+
<p id="methods-diff"></p>
44+
<h3>Fields:</h3>
45+
<p id="fields-diff"></p>
46+
</div>
47+
</div>
48+
</main>
49+
</Layout>

src/styles/global.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ input[class~="dark"] {
7070
@apply text-ornithe-text bg-ornithe-input-bg border-none p-2 w-fit;
7171
}
7272

73+
select {
74+
@apply text-ornithe-text bg-ornithe-input-bg border-none p-2 w-fit;
75+
}
76+
select[class~="dark"] {
77+
@apply text-ornithe-text bg-ornithe-input-bg border-none p-2 w-fit;
78+
}
79+
7380
/*If someone could get a tailwind.css file working I'd be grateful. -worldwidepixel*/
7481

7582
@tailwind utilities;

0 commit comments

Comments
 (0)