Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesigned Installer box + Rounded UI + Other UI Improvements #15

Merged
merged 22 commits into from
Jan 16, 2024
Merged
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
60 changes: 43 additions & 17 deletions assets/js/neoforge.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@ const FORGE_GAV = 'net/neoforged/neoforge'
const LEGACY_GAV = 'net/neoforged/forge'
const LATEST_ENDPOINT = 'https://maven.neoforged.net/api/maven/latest/version/releases/'
const DOWNLOAD_URL = 'https://maven.neoforged.net/releases'
//https://maven.neoforged.net/api/maven/latest/version/releases/net%2Fneoforged%2Fneoforge?filter=20.2
// For the latest version: https://maven.neoforged.net/api/maven/latest/version/releases/net/neoforged/neoforge
// For legacy version(s): https://maven.neoforged.net/api/maven/latest/version/releases/net/neoforged/forge
// To filter a specific MC version: https://maven.neoforged.net/api/maven/latest/version/releases/net/neoforged/neoforge?filter=20.4
async function loadLatestVersions(minecraftVersions) {
for (const mcVersion of minecraftVersions) {
let gav;
let fn;
let mcvers;
if (mcVersion.startsWith("1.20.1")) {
gav = LEGACY_GAV;
fn = "forge";
mcvers = "1.20.1"
} else {
gav = FORGE_GAV;
fn = "neoforge";
mcvers = `1.${mcVersion}`
}
let currentMcVersionUrl = new URL(LATEST_ENDPOINT + encodeURIComponent(gav) + '?filter=' + encodeURIComponent(mcVersion));
let gav;
let fn;
let mcvers;
let dropDown_VAL;
Technici4n marked this conversation as resolved.
Show resolved Hide resolved
let badges_beta;
let badges_new;
if (mcVersion.startsWith("1.20.1")) {
gav = LEGACY_GAV;
fn = "forge";
mcvers = "1.20.1";
badges_new = "";
badges_beta = "";
dropDown_VAL = "";
} else {
gav = FORGE_GAV;
fn = "neoforge";
badges_beta = "";
badges_new = `<font class="badges_new">NEW</font>`;
dropDown_VAL = ` open="open"`;
}

let currentMcVersionUrl;
let versionJson;
if (mcvers == "1.20.1") {
currentMcVersionUrl = new URL(LATEST_ENDPOINT + encodeURIComponent(gav) + '?filter=' + encodeURIComponent(mcVersion));
} else {
currentMcVersionUrl = new URL(LATEST_ENDPOINT + encodeURIComponent(gav));
}

try {
const response = await fetch(currentMcVersionUrl);
Expand All @@ -34,17 +50,27 @@ async function loadLatestVersions(minecraftVersions) {

if (versionJson) {
const {version} = versionJson;
if (mcVersion == "latest") {
mcvers = "1." + Array.from(version)[0] + Array.from(version)[1] + Array.from(version)[2] + Array.from(version)[3];
}
if (version.includes("beta")) {
badges_beta = `<font class="badges_beta">BETA</font>`;
}

const vs = `#filelist${mcVersion}`.split('.').join("");
const installerUrl = `${DOWNLOAD_URL}/${gav}/${encodeURIComponent(version)}/${fn}-${encodeURIComponent(version)}-installer.jar`;
const changelogUrl = `${DOWNLOAD_URL}/${gav}/${encodeURIComponent(version)}/${fn}-${encodeURIComponent(version)}-changelog.txt`;

document.querySelector(vs).innerHTML = `
<div class="fileinfo__header">NeoForge ${version} for Minecraft ${mcvers}</div>
<details${dropDown_VAL}>
<summary class="fileinfo__header">${badges_beta} ${badges_new} NeoForge ${version} for Minecraft ${mcvers}</summary>
<div class="fileinfo__body">
<a href="${installerUrl}"><span class="fileinfo__icon"><i class="bi-file-earmark-zip-fill" style="font-size: 2rem;"></i></span>
<span class="fileinfo__content"><span>Latest <em>NeoForge</em> Installer</span><span>${fn}-${version}-installer.jar</span></span></a>
<span class="fileinfo__content"><span>Latest <em>NeoForge</em> Installer</span><span>${fn}-${version}-installer.jar</span></span></a>
<a href="${changelogUrl}"><span class="fileinfo__icon"><i class="bi-file-earmark-text-fill" style="font-size: 2rem;"></i></span>
<span class="fileinfo__content"><span>Latest Changelog</span><span>${version}</span></span></a>
<span class="fileinfo__content"><span>Latest Changelog</span><span>${version}</span></span></a>
</div>
</details>
`;
}
}
Expand Down
6 changes: 2 additions & 4 deletions content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ description: |
# NeoForge installer files
You can find a direct link to our latest installer files below.

{{<projects>}}

{{< files "latest" >}}
{{< files "1.20.1" >}}

Note: the file is still called forge in 1.20.1 to maintain compatibility with launchers.
{{< versions >}}

# Using NeoForge for mod development

Expand Down
56 changes: 28 additions & 28 deletions content/news/20.4networking-rework.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@ public static void register(final RegisterPacketHandlerEvent event) {
.optional();
}
```
{{< notice note >}}
{{< box note >}}
The registrar is a semi-immutable object; calling `versioned(String version)` or `optional()` on an instance of the registrar will cause a new instance with the desired configuration to be created.
{{< /notice >}}
{{< /box >}}

{{< notice warning >}}
{{< box warning >}}
The registrar loses its validity the moment the scope of the event has been left. Registering payload handlers outside the event handling scope will result in those payloads not being known to the system and not being sent over the connection. Additionally, the attempt to register them outside the event scope will trigger an exception.
{{< /notice >}}
{{< /box >}}

The registrar offers six different endpoints, three pairs of two, to register new payloads. A pair exists for the play phase, one pair for the special configuration sub-phase, and one pair of methods for both.

{{< notice info >}}
{{< box info >}}
It is impossible to register custom payloads that should be sent during the login phase of the connection. And the new code offers, as such, no infrastructure to achieve this.
{{< /notice >}}
{{< /box >}}

Within each pair of registration methods for a phase, two variants (as such, six methods) are available. One that registers the same handler for both sides of the connection and one that takes a consumer, allowing for the configuration of single-sided or differentially handled payloads.

Expand All @@ -70,42 +70,42 @@ The value of the discriminator during writing is retrieved from the `CustomPacke
The value against which the id, read from the connection, is compared to find a reader, is the one that is given to the registrar as the first argument.
It is as such of the upmost importance that both the registrar and the `id()` method of the payload instance receive the same resource location.

{{< notice tip >}}
{{< box tip >}}
We recommend that you store your ID in a `public static final ResourceLocation ID = new ResourceLocation("mod_id", "payload_id")` field, and reference that in both places.
{{< /notice >}}
{{< /box >}}

{{< notice warning >}}
{{< box warning >}}
Given that the id is used as a discriminator, it is important that you use a unique value, especially for the path of each payload type. If you try to register the same id twice, the registrar will throw an exception. If you try to register an id with a namespace other than the one the registrar is for, the registrar will throw an exception. You are free to request registrars for other namespaces than your own.
{{< /notice >}}
{{< /box >}}

### Payload reading
Payload reading happens via the vanilla method, implementing the `FriendlyByteBuf.Reader<T>` functional interface. During the registration of the payload type, as can be seen above, you need to pass an implementation of this interface so that a new instance of the payload can be created by the system when a custom payload packet with it as payload arrives at the receiving end.

{{< notice tip >}}
{{< box tip >}}
As we recommend that your payload implementations are `record`s in Java, instead of classes, we also recommend that you create a custom constructor with the record, to read the records fields from the buffer. This constructor can then be passed as a method reference for the reader implementation. So if `SimplePayload(String something) {}` is your normal record, then adding `SimplePayload(FriendlyByteBuf buf) { this(buf.readUtf()); }` as a constructor to the `SimplePayload` record will allow you to pass it as a method reference `SimplePayload::new` to the registrar when it asks for an implementation of `FriendlyByteBuf.Reader<SimplePayload>`
{{< /notice >}}
{{< /box >}}

{{< notice warning >}}
{{< box warning >}}
There is no guarantee regarding which thread the reading or writing callback is invoked upon. It is as such important to note that the method can be called on many threads in parallel if the same packet is processed by many connections simultaneously.
{{< /notice >}}
{{< /box >}}

### Payload writing
The `CustomPacketPayload` interface contains a method: `write(FriendlyByteBuf)` method. This method is invoked when it is time to write your payload to the network connection. There is no guarantee regarding what thread invokes the writer.

{{< notice warning >}}
{{< box warning >}}
As with payload reading there is no guarantee regarding which thread the writing callback is invoked upon. It is as such important to note that the method can be called on many threads in parallel if the same packet is sent on many connections simultaneously.
{{< /notice >}}
{{< /box >}}

{{< notice warning >}}
{{< box warning >}}
Payloads are only read and written if sent over a connection. This means that the host of a single-player world (even if exposed to LAN) has packets and, as such, payloads transferred in memory. This means that for those payloads, no write method is invoked, and no reader is called. Only the handler is invoked!
{{< /notice >}}
{{< /box >}}

### Payload handling
Once a payload has been written, transmitted and read, the payload handler is invoked. This handler is again looked up using the id of the payload, and then invoked with the context of the receiving end. Each handler takes two arguments the: payload instance, and the context.

{{< notice warning >}}
{{< box warning >}}
Payloads are processed on the network thread, and can as such happen in parallel with other payloads of the same type being handled. If you need to ensure that the payload is processed on the main thread, serially, see the `ISynchronizedWorkHandler` available in the context under the `workHandler()` method.
{{< /notice >}}
{{< /box >}}

#### The context
The context contains information, callbacks and entry points, to access the surrounding network system, the main thread, as well as handling processing other packets, or completing configuration tasks.
Expand All @@ -116,9 +116,9 @@ The reply handler can be used to quickly send a payload back to the sender. It i
##### PacketHandler
In case you implement a packet splitting mechanism, whether that splits on full vanilla packets, or custom packet payloads, the `IPacketHandler` interface gives you access to the start of the processing pipeline, allowing you to process other payloads immediately.

{{< notice note >}}
{{< box note >}}
This does not transmit payloads, it purely allows for the receiving end to process additional packets or payloads constructed in memory during the processing of your payload.
{{< /notice >}}
{{< /box >}}

The packet handler also gives you access to a `disconnect(Component)` method, allowing you to terminate the connection, and showing the given component as reason to the user.

Expand Down Expand Up @@ -169,26 +169,26 @@ Vanilla now provides a centralized way to perform tasks and jobs that need to be

Under normal circumstances these tasks are implementations of the `ConfigurationTask` interface, with a single method: `start(Consumer<Packet<?>>)`. However, this is subpar in our situation. Modders should never really have to touch raw Packets to perform their duties, only payloads. And as such it was decided to have modders implement the `ICustomConfigurationTask` interface from NeoForge. This provides a wrapper around the `ConfigurationTask` signature and allows sending payloads instead of packets, by implementing `run(Consumer<CustomPacketPayload>)` instead. The given consumer will then automatically convert the payload to a packet and send it to the client that is being configured.

{{< notice note >}}
{{< box note >}}
In practice, an instance of `ICustomConfigurationTask` is also an instance of `ConfigurationTask` as one extends the other. But to provide the ability for it to be a functional interface, the `start` method is implemented by default. You should not override it.
{{< /notice >}}
{{< /box >}}

### OnGameConfigurationEvent
This event is fired to collect all tasks that should be run, and allows for the registration of `ICustomConfigurationTask` instances to the listener. It is not possible to register the vanilla `ConfigurationTask` instances.

{{< notice tip >}}
{{< box tip >}}
This event is fired on the mod bus, to preserve dependency order. Given that configuration tasks can only be running in order of registration, you can safely assume that configuration tasks of your dependencies have been running before yours.
{{< /notice >}}
{{< /box >}}

### NeoForge packet changes:
Moved configuration sync, registry sync, and tier registry sync to configuration phase tasks.

## Bundle packet processing
In 1.19.4 Mojang introduced the bundling system for packets, which is a core component that allows packets to be processed together. We anticipate that modders may want to use this system, so we adapted it to accept custom payloads. You will find a `sendBundled(CustomPacketPayload... payloads)` method on the `ServerGamePacketListener`.

{{< notice warning >}}
{{< box warning >}}
Packet bundling is only supported during the play phase of the network protocol. It cannot be used during the configuration phase of the protocol.
{{< /notice >}}
{{< /box >}}

## Opening menus with custom data
In the past, NeoForge supported opening UIs from the server side with additional data, via `NetworkHooks.openScreen(...)`. This system has been moved and is now part of the server `ServerPlayer` extension. You can call the method `openMenu` with the same parameters.
Expand Down
17 changes: 17 additions & 0 deletions layouts/shortcodes/box.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!--Notice Color Scheme Fix-->
<!--Credits: https://github.com/martignoni/hugo-notice-->
Technici4n marked this conversation as resolved.
Show resolved Hide resolved
{{/* Available notice types: warning, info, note, tip */}}
{{- $noticeType := .Get 0 | default "note" -}}
{{/* Workaround markdownify inconsistency for single/multiple paragraphs */}}
{{- $raw := (markdownify .Inner | chomp) -}}
{{- $block := findRE "(?is)^<(?:address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h(?:1|2|3|4|5|6)|header|hgroup|hr|li|main|nav|noscript|ol|output|p|pre|section|table|tfoot|ul|video)\\b" $raw 1 -}}
{{/* Count how many times we've called this shortcode and load the css if it's the first time */}}
{{- if not ($.Page.Scratch.Get "noticecount") -}}
<!--Modified the CSS to use the website's color scheme method--><style type="text/css">[data-theme="light"]{--root-color:#444;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#c33;--warning-content:#fee;--info-title:#fb7;--info-content:#fec;--note-title:#6be;--note-content:#e7f2fa;--tip-title:#5a5;--tip-content:#efe} [data-theme="dark"]{--root-color:#ddd;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#800;--warning-content:#400;--info-title:#a50;--info-content:#420;--note-title:#069;--note-content:#023;--tip-title:#363;--tip-content:#121} .notice{padding:18px;line-height:24px;margin-bottom:24px;border-radius:4px;color:var(--root-color);background:var(--root-background)}.notice p:last-child{margin-bottom:0}.notice-title{margin:-18px -18px 12px;padding:4px 18px;border-radius:4px 4px 0 0;font-weight:700;color:var(--title-color);background:var(--title-background)}.notice.warning .notice-title{background:var(--warning-title)}.notice.warning{background:var(--warning-content)}.notice.info .notice-title{background:var(--info-title)}.notice.info{background:var(--info-content)}.notice.note .notice-title{background:var(--note-title)}.notice.note{background:var(--note-content)}.notice.tip .notice-title{background:var(--tip-title)}.notice.tip{background:var(--tip-content)}.icon-notice{display:inline-flex;align-self:center;margin-right:8px}.icon-notice img,.icon-notice svg{height:1em;width:1em;fill:currentColor}.icon-notice img,.icon-notice.baseline svg{top:.125em;position:relative}</style>
<div><svg width="0" height="0" display="none" xmlns="http://www.w3.org/2000/svg"><symbol id="tip-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"><path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"/></symbol><symbol id="note-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"><path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/></symbol><symbol id="warning-notice" viewBox="0 0 576 512" preserveAspectRatio="xMidYMid meet"><path d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/></symbol><symbol id="info-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"><path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"/></symbol></svg></div>
{{- end -}}
{{- $.Page.Scratch.Add "noticecount" 1 -}}
<div class="notice {{ $noticeType }}" {{ if len .Params | eq 2 }} id="{{ .Get 1 }}" {{ end }}>
<p class="first notice-title"><span class="icon-notice baseline"><svg><use href="#{{- $noticeType -}}-notice"></use></svg></span>{{- i18n $noticeType -}}</p>
{{- if or $block (not $raw) }}{{ $raw }}{{ else }}<p>{{ $raw }}</p>{{ end -}}
</div>
26 changes: 15 additions & 11 deletions layouts/shortcodes/files.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
{{ .Page.Scratch.Set "nfJS" "hi" }}
{{ end }}

<div id="filelist{{ replace (.Get 0) "." "" }}" class="fileinfo">
UltimatChamp marked this conversation as resolved.
Show resolved Hide resolved
<div class="fileinfo__header">NeoForge (loading version...)</div>
<div class="fileinfo__body">
<a><span>Latest <em>NeoForge</em> Installer</span><span class="fileinfo__placeholder">neoforge-0.00.00-00.00.00-installer.jar</span></a>
<a><span>Latest Changelog</span><span class="fileinfo__placeholder">0.00.00-00.00.00</span></a>

<div id="filelist{{ replace (.Get 0) "." "" }}" class="fileinfo">
<details>
<summary class="fileinfo__header">NeoForge (loading version...)</summary>
<div class="fileinfo__body">
<a><span>Latest <em>NeoForge</em> Installer</span><span class="fileinfo__placeholder">neoforge-0.00.00-00.00.00-installer.jar</span></a>
<a><span>Latest Changelog</span><span class="fileinfo__placeholder">0.00.00-00.00.00</span></a>
</div>
</details>
</div>
</div>

<script>
document.addEventListener("readystatechange", evt=>{
if (evt.target.readyState === "complete") {
loadLatestVersions([{{ .Get 0 }}]);
}
});
document.addEventListener("readystatechange", evt=>{
if (evt.target.readyState === "complete") {
loadLatestVersions([{{ .Get 0 }}]);
}
});
</script>
Loading