-
Notifications
You must be signed in to change notification settings - Fork 506
The Mod Protocol API, aka better validation of mod presence of both-side mods with extra protocol version syncing #4011
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
base: 1.21.1
Are you sure you want to change the base?
Changes from all commits
605d858
5d1f970
dbcdcaa
685051a
d16dbc4
781a8da
822bdb2
73affdf
a770fb7
175d392
a08bffa
ae341a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| version = getSubprojectVersion(project) | ||
|
|
||
| moduleDependencies(project, ['fabric-api-base', 'fabric-networking-api-v1']) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * 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 net.fabricmc.fabric.api.client.modprotocol.v1; | ||
|
|
||
| import it.unimi.dsi.fastutil.objects.Object2IntMap; | ||
|
|
||
| import net.minecraft.client.network.ClientCommonNetworkHandler; | ||
| import net.minecraft.network.ClientConnection; | ||
| import net.minecraft.server.network.ServerCommonNetworkHandler; | ||
| import net.minecraft.util.Identifier; | ||
|
|
||
| import net.fabricmc.fabric.api.modprotocol.v1.ModProtocol; | ||
| import net.fabricmc.fabric.api.modprotocol.v1.ModProtocolIds; | ||
| import net.fabricmc.fabric.impl.modprotocol.RemoteProtocolStorage; | ||
|
|
||
| /** | ||
| * Utility methods allowing to get protocol versions supported by the server. | ||
| * | ||
| * <p>Protocol identifiers can be any valid {@link Identifier}. The default is {@code mod:(mod ID)}. | ||
| * @see ModProtocolIds | ||
| */ | ||
| public final class ClientModProtocolLookup { | ||
| /** | ||
| * A protocol version returned by {@code getSupportedProtocol} methods, when the server doesn't support the requested protocol. | ||
| */ | ||
| public static final int UNSUPPORTED = -1; | ||
| private ClientModProtocolLookup() { } | ||
|
|
||
| /** | ||
| * Gets the protocol version supported by the server. | ||
| * | ||
| * @param handler the network handler connected to the server | ||
| * @param protocolId protocol's id | ||
| * @return the protocol version supported by the server | ||
| */ | ||
| public static int getSupportedProtocol(ClientCommonNetworkHandler handler, Identifier protocolId) { | ||
| return RemoteProtocolStorage.getProtocol(handler, protocolId); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the protocol version supported by the server. | ||
| * | ||
| * @param connection the ClientConnection connected to the server | ||
| * @param protocolId protocol's id | ||
| * @return the protocol version supported by the server | ||
| */ | ||
| public static int getSupportedProtocol(ClientConnection connection, Identifier protocolId) { | ||
| return RemoteProtocolStorage.getProtocol(connection, protocolId); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the protocol version supported by the server. | ||
| * | ||
| * @param handler the network handler connected to the server | ||
| * @param protocol protocol to check against | ||
| * @return the protocol version supported by the server | ||
| */ | ||
| public static int getSupportedProtocol(ClientCommonNetworkHandler handler, ModProtocol protocol) { | ||
| return RemoteProtocolStorage.getProtocol(handler, protocol.id()); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the protocol version supported by the server. | ||
| * | ||
| * @param connection the ClientConnection connected to the server | ||
| * @param protocol protocol to check against | ||
| * @return the protocol version supported by the server | ||
| */ | ||
| public static int getSupportedProtocol(ClientConnection connection, ModProtocol protocol) { | ||
| return RemoteProtocolStorage.getProtocol(connection, protocol.id()); | ||
| } | ||
|
|
||
| /** | ||
| * Gets all protocols supported by the server. | ||
| * | ||
| * @param handler the network handler connected to the server | ||
| * @return the map of protocols to the versions supported by the server | ||
| */ | ||
| public static Object2IntMap<Identifier> getAllSupportedProtocols(ServerCommonNetworkHandler handler) { | ||
| return RemoteProtocolStorage.getMap(handler); | ||
| } | ||
|
|
||
| /** | ||
| * Gets all protocols supported by the server. | ||
| * | ||
| * @param connection the ClientConnection connected to the server | ||
| * @return the map of protocols to the versions supported by the server | ||
| */ | ||
| public static Object2IntMap<Identifier> getAllSupportedProtocols(ClientConnection connection) { | ||
| return RemoteProtocolStorage.getMap(connection); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| /** | ||
| * The Mod Protocol API (client side), version 1. | ||
| * | ||
| * <p>See {@link net.fabricmc.fabric.api.modprotocol.v1}</p> | ||
| */ | ||
|
|
||
| @ApiStatus.Experimental | ||
| package net.fabricmc.fabric.api.client.modprotocol.v1; | ||
modmuss50 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import org.jetbrains.annotations.ApiStatus; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * 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 net.fabricmc.fabric.impl.modprotocol.client; | ||
|
|
||
| import java.util.HashMap; | ||
|
|
||
| import net.minecraft.util.Identifier; | ||
|
|
||
| import net.fabricmc.api.ClientModInitializer; | ||
| import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking; | ||
| import net.fabricmc.fabric.impl.modprotocol.ModProtocolImpl; | ||
| import net.fabricmc.fabric.impl.modprotocol.ModProtocolInit; | ||
| import net.fabricmc.fabric.impl.modprotocol.ModProtocolManager; | ||
| import net.fabricmc.fabric.impl.modprotocol.RemoteProtocolStorage; | ||
| import net.fabricmc.fabric.impl.modprotocol.payload.ModProtocolRequestS2CPayload; | ||
| import net.fabricmc.fabric.impl.modprotocol.payload.ModProtocolResponseC2SPayload; | ||
|
|
||
| public final class ClientModProtocolInit implements ClientModInitializer { | ||
| public void onInitializeClient() { | ||
| ClientConfigurationNetworking.registerGlobalReceiver(ModProtocolRequestS2CPayload.ID, (payload, context) -> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use |
||
| var map = new HashMap<Identifier, ModProtocolImpl>(payload.modProtocol().size()); | ||
|
|
||
| for (ModProtocolImpl protocol : payload.modProtocol()) { | ||
| map.put(protocol.id(), protocol); | ||
| } | ||
|
|
||
| ModProtocolManager.ValidationResult validate = ModProtocolManager.validateClient(map); | ||
|
|
||
| if (validate.isSuccess()) { | ||
| ((RemoteProtocolStorage) context.networkHandler()).fabric$setRemoteProtocol(validate.supportedProtocols()); | ||
| context.responseSender().sendPacket(new ModProtocolResponseC2SPayload(validate.supportedProtocols())); | ||
| return; | ||
| } | ||
|
|
||
| var b = new StringBuilder(); | ||
| b.append("Disconnected due to mismatched protocols!").append('\n'); | ||
| b.append("Missing entries:").append('\n'); | ||
| ModProtocolManager.appendTextEntries(validate.missing(), ModProtocolManager.LOCAL_MOD_PROTOCOLS_BY_ID, -1, text -> b.append(" - ").append(text.getString())); | ||
|
|
||
| context.responseSender().disconnect(ModProtocolManager.constructMessage(validate.missing(), ModProtocolManager.LOCAL_MOD_PROTOCOLS_BY_ID)); | ||
| ModProtocolInit.LOGGER.warn(b.toString()); | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * 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 net.fabricmc.fabric.mixin.modprotocol.client; | ||
|
|
||
| import it.unimi.dsi.fastutil.objects.Object2IntMap; | ||
| import org.spongepowered.asm.mixin.Final; | ||
| import org.spongepowered.asm.mixin.Mixin; | ||
| import org.spongepowered.asm.mixin.Shadow; | ||
|
|
||
| import net.minecraft.client.network.ClientCommonNetworkHandler; | ||
| import net.minecraft.network.ClientConnection; | ||
| import net.minecraft.util.Identifier; | ||
|
|
||
| import net.fabricmc.fabric.impl.modprotocol.RemoteProtocolStorage; | ||
|
|
||
| @Mixin(ClientCommonNetworkHandler.class) | ||
| public class ClientCommonNetworkHandlerMixin implements RemoteProtocolStorage { | ||
| @Shadow | ||
| @Final | ||
| protected ClientConnection connection; | ||
|
|
||
| @Override | ||
| public Object2IntMap<Identifier> fabric$getRemoteProtocol() { | ||
| return ((RemoteProtocolStorage) this.connection).fabric$getRemoteProtocol(); | ||
| } | ||
|
|
||
| @Override | ||
| public void fabric$setRemoteProtocol(Object2IntMap<Identifier> protocol) { | ||
| ((RemoteProtocolStorage) this.connection).fabric$setRemoteProtocol(protocol); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * 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 net.fabricmc.fabric.mixin.modprotocol.client; | ||
|
|
||
| import org.spongepowered.asm.mixin.Mixin; | ||
| import org.spongepowered.asm.mixin.injection.At; | ||
| import org.spongepowered.asm.mixin.injection.Inject; | ||
| import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | ||
|
|
||
| import net.minecraft.client.MinecraftClient; | ||
| import net.minecraft.client.network.ClientCommonNetworkHandler; | ||
| import net.minecraft.client.network.ClientConfigurationNetworkHandler; | ||
| import net.minecraft.client.network.ClientConnectionState; | ||
| import net.minecraft.network.ClientConnection; | ||
| import net.minecraft.network.packet.s2c.config.SelectKnownPacksS2CPacket; | ||
|
|
||
| import net.fabricmc.fabric.impl.modprotocol.ModProtocolManager; | ||
| import net.fabricmc.fabric.impl.modprotocol.RemoteProtocolStorage; | ||
|
|
||
| @Mixin(ClientConfigurationNetworkHandler.class) | ||
| public abstract class ClientConfigurationNetworkHandlerMixin extends ClientCommonNetworkHandler { | ||
| protected ClientConfigurationNetworkHandlerMixin(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) { | ||
| super(client, connection, connectionState); | ||
| } | ||
|
|
||
| @Inject(method = "onSelectKnownPacks", at = @At("HEAD"), cancellable = true) | ||
| private void preventJoiningIncompatibleServers(SelectKnownPacksS2CPacket packet, CallbackInfo ci) { | ||
| if (((RemoteProtocolStorage) this.connection).fabric$getRemoteProtocol() == null && !ModProtocolManager.SERVER_REQUIRED.isEmpty()) { | ||
| this.client.execute(() -> this.connection.disconnect(ModProtocolManager.constructMessage(ModProtocolManager.SERVER_REQUIRED, ModProtocolManager.LOCAL_MOD_PROTOCOLS_BY_ID))); | ||
| ci.cancel(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| { | ||
| "required": true, | ||
| "package": "net.fabricmc.fabric.mixin.modprotocol.client", | ||
| "compatibilityLevel": "JAVA_17", | ||
| "mixins": [ | ||
| "ClientCommonNetworkHandlerMixin" | ||
| ], | ||
| "injectors": { | ||
| "defaultRequire": 1 | ||
| }, | ||
| "client": [ | ||
| "ClientConfigurationNetworkHandlerMixin" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * 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 net.fabricmc.fabric.api.modprotocol.v1; | ||
|
|
||
| import it.unimi.dsi.fastutil.ints.IntList; | ||
| import org.jetbrains.annotations.ApiStatus; | ||
|
|
||
| import net.minecraft.util.Identifier; | ||
|
|
||
| /** | ||
| * Interface representing registered ModProtocol. Can be used for further lookups. | ||
| */ | ||
| @ApiStatus.NonExtendable | ||
| public interface ModProtocol { | ||
| /** | ||
| * @return Identifier associated with this Mod Protocol | ||
| */ | ||
| Identifier id(); | ||
|
Comment on lines
+29
to
+32
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question (ive not looked at the impl), where does this ID come from when a protocol is specified in the fabric.mod.json?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By default it's "mod:(modid)", but can be overriten with any other identifier |
||
| /** | ||
| * @return Display name of this protocol | ||
| */ | ||
| String name(); | ||
| /** | ||
| * @return Display version of this protocol | ||
| */ | ||
| String version(); | ||
|
Comment on lines
+33
to
+40
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Somewhat realted to my above question, why are these needed, would it be better tieing this to the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since (currently) mod can provide multiple protocols (or make them in code) it still makes sense for this to exist. As a bonus people might want to have it different/more specific with something like
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do think tieing it to the mod solves a lot of problems mostly by removing a lot of duplicated data from the fmj. You also get support for "provides" for free. |
||
| /** | ||
| * @return Protocol versions supported by this protocol | ||
| */ | ||
| IntList protocol(); | ||
modmuss50 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * @return Client requirement of this protocol | ||
| */ | ||
| boolean requireClient(); | ||
| /** | ||
| * @return Server requirement of this protocol | ||
| */ | ||
| boolean requireServer(); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.