Files
shacraft-launcher/admission-client/src/client/java/ru/shacraft/admission/ClientAdmission.java
T

58 lines
3.6 KiB
Java

package ru.shacraft.admission;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicBoolean;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents;
import net.fabricmc.fabric.impl.networking.RegistrationPayload;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import java.util.List;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.minecraft.network.chat.Component;
/** The account session remains in the native launcher, never in Minecraft. */
public final class ClientAdmission implements ClientModInitializer {
private static final AtomicBoolean USED = new AtomicBoolean();
@Override public void onInitializeClient() {
PayloadTypeRegistry.clientboundConfiguration().register(AdmissionPayloads.Challenge.TYPE, AdmissionPayloads.Challenge.CODEC);
PayloadTypeRegistry.serverboundConfiguration().register(AdmissionPayloads.Proof.TYPE, AdmissionPayloads.Proof.CODEC);
ClientConfigurationNetworking.registerGlobalReceiver(AdmissionPayloads.Challenge.TYPE, ClientAdmission::challenge);
// Paper waits for vanilla channel advertisement, while Fabric normally waits
// for the server's registration first. Bootstrap our one fixed receiver.
// INIT runs in the listener constructor; schedule() queues until after vanilla
// has switched the outbound protocol from LOGIN to CONFIGURATION.
ClientConfigurationConnectionEvents.INIT.register((listener, client) -> client.schedule(() ->
listener.send(new ServerboundCustomPayloadPacket(new RegistrationPayload(
RegistrationPayload.REGISTER, List.of(AdmissionPayloads.Challenge.TYPE.id()))))));
}
private static void challenge(AdmissionPayloads.Challenge challenge, ClientConfigurationNetworking.Context context) {
var connection = context.packetContext().orElseThrow(net.fabricmc.fabric.api.networking.v1.context.PacketContext.CONNECTION);
boolean loopback = "1".equals(System.getenv("SHACRAFT_ADMISSION_ALLOW_LOOPBACK"));
if (!(connection.getRemoteAddress() instanceof InetSocketAddress remote)
|| remote.getAddress() == null
|| !AdmissionProof.allowedTarget(remote.getAddress().getHostAddress(), remote.getPort(), loopback)
|| !AdmissionProof.SERVER_ID.equals(challenge.serverId())
|| !context.client().getUser().getName().equals(challenge.nickname())
|| !AdmissionProof.validOpaque(challenge.nonce())) {
deny(context); return;
}
String ticket = System.getenv("SHACRAFT_ADMISSION_TICKET");
String privateKey = System.getenv("SHACRAFT_ADMISSION_PRIVATE_KEY");
if (!AdmissionProof.validOpaque(ticket) || !USED.compareAndSet(false, true)) {
deny(context); return;
}
try {
String signature = AdmissionProof.sign(privateKey, ticket, challenge.serverId(), challenge.nickname(), challenge.nonce());
context.responseSender().sendPacket(new AdmissionPayloads.Proof(ticket, signature));
} catch (Exception invalidKey) { deny(context); }
}
private static void deny(ClientConfigurationNetworking.Context context) {
context.responseSender().disconnect(Component.literal(
"Не удалось подтвердить вход ShaCraft. Закройте игру и запустите её заново через ShaCraft Launcher."));
}
}