Skip to content

Commit

Permalink
mod protocol -> modpack protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
TheGlitch76 committed Jun 1, 2024
1 parent bdbd7d9 commit 8b18ad5
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 100 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright 2023 The Quilt Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.quiltmc.qsl.registry.api.sync;

import net.minecraft.server.network.ServerPlayNetworkHandler;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import net.minecraft.server.network.ServerConfigurationNetworkHandler;

import org.quiltmc.loader.api.ModContainer;
import org.quiltmc.qsl.registry.impl.sync.mod_protocol.ModProtocolImpl;
import org.quiltmc.qsl.registry.impl.sync.server.ExtendedConnectionClient;

/**
* Utilities for getting information about active Mod Protocols, a system for requiring compatible versions
* of a mod or modpack to be installed on the client when connecting to a server.
* <p>
* <h1>The Mod Protocol System</h1>
* <h2>Overview</h2>
* The mod protocol system allows mods and modpacks to require specific version ranges (represented as a protocol integer)
* to be present on both sides when joining a server.
* <p>
* When a client pings a server or attempts to connect, the server sends to the client a list of ALL mod protocols, containing
* each one's id, display name and a list of supported protocol versions. Upon connection, the client replies with a list
* of the highest version that is supported by the client and server for each protocol. The negotiated protocol version can
* be queried with {@link ModProtocols#getSupported(ServerConfigurationNetworkHandler, ModContainer)}.
* <p>
* If a client does not support any of the protocol versions requested by the server, then the client will not be allowed
* to connect (unless the mod protocol is marked optional).
* <h2>Configuration</h2>
* Mods can define a mod protocol in their {@code quilt.mod.json}.
* <pre>{@code
* {
* "quilt_loader": {...},
* "quilt_registry": {
* // May be a single number or an array of supported versions.
* "mod_protocol": [1, 2, 4]
* }
* }
* }</pre>
*
* The id of the protocol is identical to the mod id. The display name is always the mod's display name (or ID if none exists)
* followed by the version, e.g. "Quilt Registry API v4.1.0"
* Protocols can also be marked optional, which means they are not required to be supported by both sides in order
* to connect:
* <pre>{@code
* {
* "quilt_loader": {...},
* "quilt_registry": {
* "mod_protocol": {
* "value": 1,
* "optional": true
* }
* }
* }
* }</pre>
* <p>
* In addition to mods defining their own mod protocols, users can define a special modpack protocol in the Quilt Registry
* config (located at {@code configs/quilt/qsl/registry.toml}). Other than being defined by the user, the modpack protocol
* is otherwise identical to standard mod protocols.
*/
@ApiStatus.Experimental
public final class ModProtocols {
public static final int UNSUPPORTED = -1;
private ModProtocols() {}

/**
* {@return {@code true} if a modpack protocol has been provided through the config, or {@code false} otherwise}
* */
@Contract(pure = true)
public static boolean isModpackProtocolEnabled() {
return ModProtocolImpl.modpackDef != null;
}

/**
* {@return a string representing the modpack protocol id, or {@code null} otherwise}
*/
@Contract(pure = true)
@Nullable
public static String getModpackProtocolId() {
return isModpackProtocolEnabled() ? ModProtocolImpl.modpackDef.id() : null;
}

/**
* {@return a string representing the modpack protocol display name, or {@code null} otherwise}
*/
@Contract(pure = true)
@Nullable
public static String getModpackDisplayName() {
return isModpackProtocolEnabled() ? ModProtocolImpl.modpackDef.displayName() : null;
}

/**
* Checks the modpack protocol version supported by a given player.
*
* @param handler the player's network handler
* @return the latest modpack version supported by the player. -1 if not supported
*/
@Contract(pure = true)
public static int getSupportedModpack(@NotNull ServerConfigurationNetworkHandler handler) {
return isModpackProtocolEnabled() ? ExtendedConnectionClient.from(handler).quilt$getModProtocol("modpack:" + getModpackProtocolId()) : UNSUPPORTED;
}

/**
* Checks the mod protocol version supported by a given player.
*
* @param handler the player's network handler
* @param modContainer ModContainer defining protocol version
* @return latest supported by player protocol for mod. -1 if not supported
*/
@Contract(pure = true)
public static int getSupported(@NotNull ServerConfigurationNetworkHandler handler, @NotNull ModContainer modContainer) {
return ExtendedConnectionClient.from(handler).quilt$getModProtocol("mod:" + modContainer.metadata().id());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@ public class RegistryConfig extends ReflectiveConfig {

public static class RegistrySync extends Section {
@Comment("""
Mod protocol is a feature allowing you to prevent clients with mismatched settings to join.
Client with mismatched values won't be able to connect to servers having this enabled.
It should be used only for non-vanilla compatible modpacks!
Protocol version. Needs to be the same on client and server. If it has value of -1, it won't be required by servers.
The modpack protocol is a feature allowing you to prevent clients with mismatched settings from joining.
It can be used to require a specific version of a specific Quilt modpack.
Modpack protocol version. Needs to be the same on client and server. If it has value of -1, it won't be required by servers.
""")
public final TrackedValue<Integer> mod_protocol_version = value(-1);
@Comment("Protocol id. It should be different for every modpack, to prevent joining with mismatched mods.")
public final TrackedValue<String> mod_protocol_id = value("my_quilt_modpack");
@Comment("A mod protocol name. Used for easier identification. Doesn't effect functionality")
public final TrackedValue<String> mod_protocol_name = value("My Quilt Modpack");
public final TrackedValue<Integer> modpack_protocol_version = value(-1);
@Comment("Modpack protocol id. It should be different for every modpack, to prevent joining with mismatched mods.")
public final TrackedValue<String> modpack_protocol_id = value("my_quilt_modpack");
@Comment("A modpack protocol name. Used for easier identification. Doesn't effect functionality")
public final TrackedValue<String> modpack_protocol_name = value("My Quilt Modpack");


@Comment("Message displayed for players joining with clients incompatible with Registry Sync. Supports strings and Minecraft's JSON text format.")
public final TrackedValue<String> missing_registry_sync_message = value(Text.SerializationUtil.toJson(Text.translatableWithFallback("qsl.registry_sync.unsupported_client", """
Unsupported (vanilla?) client!
This server requires modded client to join!
This server requires a modded client to join!
"""), DynamicRegistryManager.EMPTY));

@Comment("Top part of the message displayed for players joining with incompatible clients. Supports strings and Minecraft's JSON text format.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,15 @@ public static Text missingRegistry(Identifier identifier, boolean exists) {

public static Text unsupportedModVersion(List<ModProtocolDef> unsupported, ModProtocolDef missingPrioritized) {
if (missingPrioritized != null && !missingPrioritized.versions().isEmpty()) {
var x = Text.translatableWithFallback("quilt.core.registry_sync.require_main_mod_protocol", "This server requires %s with protocol version of %s!",
var x = Text.translatableWithFallback("quilt.core.registry_sync.require_modpack_protocol", "This server requires %s with protocol version of %s!",
Text.literal(missingPrioritized.displayName()).formatted(Formatting.YELLOW),
missingPrioritized.versions().getInt(0)
);

if (ModProtocolImpl.enabled && ModProtocolImpl.prioritizedEntry != null) {
if (ModProtocolImpl.enabled && ModProtocolImpl.modpackDef != null) {
x.append("\n").append(
Text.translatableWithFallback("quilt.core.registry_sync.main_mod_protocol", "You are on %s with protocol version of %s.",
Text.literal(ModProtocolImpl.prioritizedEntry.displayName()).formatted(Formatting.GOLD), ModProtocolImpl.prioritizedEntry.versions().getInt(0)
Text.translatableWithFallback("quilt.core.registry_sync.modpack_protocol", "You are on %s with protocol version of %s.",
Text.literal(ModProtocolImpl.modpackDef.displayName()).formatted(Formatting.GOLD), ModProtocolImpl.modpackDef.versions().getInt(0)
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@
import org.quiltmc.loader.api.LoaderValue;
import org.quiltmc.loader.api.ModContainer;
import org.quiltmc.loader.api.QuiltLoader;
import org.quiltmc.loader.api.Version;
import org.quiltmc.qsl.registry.impl.RegistryConfig;

public class ModProtocolImpl {
public static boolean enabled = false;
public static boolean disableQuery = false;
public static String prioritizedId = "";
public static ModProtocolDef prioritizedEntry;
public static ModProtocolDef modpackDef;
private static final Logger LOGGER = LogUtils.getLogger();

private static final Map<String, ModProtocolDef> PROTOCOL_VERSIONS = new HashMap<>();
Expand All @@ -47,10 +48,10 @@ public static void loadVersions() {
var config = RegistryConfig.INSTANCE.registry_sync;
disableQuery = config.disable_mod_protocol_ping.value();

if (config.mod_protocol_version.value() >= 0) {
prioritizedEntry = new ModProtocolDef("global:" + config.mod_protocol_id.value(), config.mod_protocol_name.value(), IntList.of(config.mod_protocol_version.value()), false);
prioritizedId = prioritizedEntry.id();
add(prioritizedEntry);
if (config.modpack_protocol_version.value() >= 0) {
modpackDef = new ModProtocolDef("modpack:" + config.modpack_protocol_id.value(), config.modpack_protocol_name.value(), IntList.of(config.modpack_protocol_version.value()), false);
prioritizedId = modpackDef.id();
add(modpackDef);
}

for (var container : QuiltLoader.getAllMods()) {
Expand Down Expand Up @@ -90,17 +91,26 @@ public static void loadVersions() {
var version = decodeVersion(".value", container, object.get("value"));

if (version != null) {
add(new ModProtocolDef("mod:" + data.id(), data.name(), version, optional));
add(new ModProtocolDef("mod:" + data.id(), data.name() + " " + getVersionString(data.version()), version, optional));
}
} else {
var version = decodeVersion("", container, value);
if (version != null) {
add(new ModProtocolDef("mod:" + data.id(), data.name(), version, false));
add(new ModProtocolDef("mod:" + data.id(), data.name() + " " + getVersionString(data.version()), version, false));
}
}
}
}

private static String getVersionString(Version version) {
String ret = version.toString();
if (Character.isDigit(ret.charAt(0))) {
return "v" + ret;
} else {
return ret;
}
}

private static IntList decodeVersion(String path, ModContainer container, LoaderValue value) {
if (value == null) {
invalidEntryType(path, container, LoaderValue.LType.NUMBER, LoaderValue.LType.NULL);
Expand Down Expand Up @@ -140,11 +150,11 @@ private static IntList decodeVersion(String path, ModContainer container, Loader
}

private static void invalidEntryType(String path, ModContainer c, LoaderValue.LType expected, LoaderValue.LType found) {
LOGGER.warn("Mod {} ({}) contains invalid 'quilt_registry.mod_protocol{}' entry! Expected '{}', found '{}'", path, c.metadata().name(), c.metadata().id(), expected.name(), found.name());
LOGGER.warn("Mod {} ({}) contains invalid 'quilt_registry.modpack_protocol{}' entry! Expected '{}', found '{}'", path, c.metadata().name(), c.metadata().id(), expected.name(), found.name());
}

private static void negativeEntry(String path, ModContainer c, int i) {
LOGGER.warn("Mod {} ({}) contains invalid 'quilt_registry.mod_protocol{}' entry! Protocol requires non-negative integer, found '{}'!", path, c.metadata().name(), c.metadata().id(), i);
LOGGER.warn("Mod {} ({}) contains invalid 'quilt_registry.modpack_protocol{}' entry! Protocol requires non-negative integer, found '{}'!", path, c.metadata().name(), c.metadata().id(), i);
}

public static IntList getVersion(String string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"quilt.core.registry_sync.more": "%s more...",
"quilt.core.registry_sync.and": " and ",
"quilt.core.registry_sync.unsupported_mod_protocol": "Unsupported mod protocol versions for:\n%s",
"quilt.core.registry_sync.require_main_mod_protocol": "This server requires %s with protocol version of %s",
"quilt.core.registry_sync.main_mod_protocol" : "You are on %s with protocol version of %s."
"quilt.core.registry_sync.require_modpack_protocol": "This server requires %s with protocol version of %s",
"quilt.core.registry_sync.modpack_protocol": "You are on %s with protocol version of %s."
}

0 comments on commit 8b18ad5

Please sign in to comment.