2025-07-18 01:14:08 +02:00
|
|
|
|
package com.cimeyclust.ezcheat.client;
|
|
|
|
|
|
|
|
|
|
|
|
import com.cimeyclust.ezcheat.Ezcheat;
|
|
|
|
|
|
import com.cimeyclust.ezcheat.EzcheatPayload;
|
|
|
|
|
|
import net.fabricmc.api.ClientModInitializer;
|
|
|
|
|
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
|
|
|
|
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
|
|
|
|
|
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
|
|
|
|
|
import net.fabricmc.loader.api.FabricLoader;
|
2025-07-18 02:18:15 +02:00
|
|
|
|
import net.fabricmc.loader.api.metadata.ModOrigin;
|
2025-07-18 01:14:08 +02:00
|
|
|
|
import org.apache.commons.codec.digest.DigestUtils;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
public class EzcheatClient implements ClientModInitializer {
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public void onInitializeClient() {
|
|
|
|
|
|
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
2025-07-18 02:18:15 +02:00
|
|
|
|
Ezcheat.LOGGER.info("Joined server: {}", handler.getConnection().getAddress());
|
|
|
|
|
|
Ezcheat.LOGGER.info("Is Singleplayer: {}", client.isInSingleplayer());
|
2025-07-18 01:14:08 +02:00
|
|
|
|
if (!client.isInSingleplayer()) {
|
2025-07-18 02:18:15 +02:00
|
|
|
|
|
|
|
|
|
|
Ezcheat.LOGGER.info("Sending mod hashes to server");
|
2025-07-18 01:14:08 +02:00
|
|
|
|
this.sendHashes();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Called client‑side to compute & send hashes */
|
|
|
|
|
|
private void sendHashes() {
|
|
|
|
|
|
// gather mod files
|
|
|
|
|
|
Path modsDir = FabricLoader.getInstance()
|
|
|
|
|
|
.getGameDir()
|
|
|
|
|
|
.resolve("mods")
|
|
|
|
|
|
.toAbsolutePath();
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, String> hashes = new HashMap<>();
|
|
|
|
|
|
for (var mod : FabricLoader.getInstance().getAllMods()) {
|
|
|
|
|
|
try {
|
2025-07-18 02:18:15 +02:00
|
|
|
|
var origin = mod.getOrigin();
|
|
|
|
|
|
// Skip nested origins
|
|
|
|
|
|
if (origin.getKind() == ModOrigin.Kind.NESTED) {
|
|
|
|
|
|
Ezcheat.LOGGER.debug("Skipping nested mod {}", mod.getMetadata().getId());
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Path modFile = null;
|
|
|
|
|
|
for (Path p : origin.getPaths()) {
|
2025-07-18 01:14:08 +02:00
|
|
|
|
if (Files.isRegularFile(p)) {
|
2025-07-18 02:18:15 +02:00
|
|
|
|
modFile = p;
|
2025-07-18 01:14:08 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-18 02:18:15 +02:00
|
|
|
|
if (modFile == null) {
|
2025-07-18 01:14:08 +02:00
|
|
|
|
Ezcheat.LOGGER.debug("Skipping mod {}: No valid path", mod.getMetadata().getId());
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-18 02:18:15 +02:00
|
|
|
|
if (modFile.startsWith(modsDir)) {
|
|
|
|
|
|
try (InputStream in = Files.newInputStream(modFile)) {
|
2025-07-18 01:14:08 +02:00
|
|
|
|
hashes.put(mod.getMetadata().getId(), DigestUtils.sha256Hex(in));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
Ezcheat.LOGGER.warn("Could not hash {}: {}", mod.getMetadata().getId(), e.getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EzcheatPayload payload = new EzcheatPayload(hashes);
|
|
|
|
|
|
ClientPlayNetworking.send(payload);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|