diff --git a/build.gradle b/build.gradle index 8c0fc87a..25fb1179 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ repositories { maven { url "https://maven.seedfinding.com/" } maven { url "https://maven-snapshots.seedfinding.com/" } maven { url "https://jitpack.io" } + maven { url "https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1" } } configurations { @@ -52,6 +53,10 @@ dependencies { includedLibrary('com.seedfinding:mc_reversal:ca64c0890c106f1a2207623c316fce86f250b918') { transitive = false } includedLibrary('com.seedfinding:latticg:1.06') + + modRuntimeOnly('me.djtheredstoner:DevAuth-fabric:1.1.0') { + exclude group: 'net.fabricmc', module: 'fabric-loader' + } } processResources { @@ -95,4 +100,4 @@ publishing { // The repositories here will be used for publishing your artifact, not for // retrieving dependencies. } -} \ No newline at end of file +} diff --git a/src/main/java/kaptainwutax/seedcrackerX/SeedCracker.java b/src/main/java/kaptainwutax/seedcrackerX/SeedCracker.java index edb68110..d20f1add 100644 --- a/src/main/java/kaptainwutax/seedcrackerX/SeedCracker.java +++ b/src/main/java/kaptainwutax/seedcrackerX/SeedCracker.java @@ -6,6 +6,7 @@ import kaptainwutax.seedcrackerX.cracker.storage.DataStorage; import kaptainwutax.seedcrackerX.finder.FinderQueue; import kaptainwutax.seedcrackerX.init.ClientCommands; +import kaptainwutax.seedcrackerX.util.Database; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.loader.api.FabricLoader; @@ -32,6 +33,8 @@ public void onInitialize() { entrypoints.add(entrypoint.getEntrypoint())); ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> ClientCommands.registerCommands(dispatcher)); + + Database.fetchSeeds(); } public DataStorage getDataStorage() { diff --git a/src/main/java/kaptainwutax/seedcrackerX/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/kaptainwutax/seedcrackerX/mixin/ClientPlayNetworkHandlerMixin.java index f2149921..2da6f769 100644 --- a/src/main/java/kaptainwutax/seedcrackerX/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/kaptainwutax/seedcrackerX/mixin/ClientPlayNetworkHandlerMixin.java @@ -1,6 +1,5 @@ package kaptainwutax.seedcrackerX.mixin; -import com.mojang.brigadier.CommandDispatcher; import kaptainwutax.seedcrackerX.SeedCracker; import kaptainwutax.seedcrackerX.config.Config; import kaptainwutax.seedcrackerX.config.StructureSave; @@ -8,11 +7,12 @@ import kaptainwutax.seedcrackerX.cracker.HashedSeedData; import kaptainwutax.seedcrackerX.finder.FinderQueue; import kaptainwutax.seedcrackerX.finder.ReloadFinders; +import kaptainwutax.seedcrackerX.util.Database; import kaptainwutax.seedcrackerX.util.Log; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.world.ClientWorld; -import net.minecraft.command.CommandSource; +import net.minecraft.network.ClientConnection; import net.minecraft.network.packet.s2c.play.ChunkDataS2CPacket; import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket; @@ -20,6 +20,7 @@ import net.minecraft.world.dimension.DimensionType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -29,8 +30,8 @@ public abstract class ClientPlayNetworkHandlerMixin { @Shadow private ClientWorld world; - @Shadow - private CommandDispatcher commandDispatcher; + + @Shadow public abstract ClientConnection getConnection(); @Inject(method = "onChunkData", at = @At(value = "TAIL")) private void onChunkData(ChunkDataS2CPacket packet, CallbackInfo ci) { @@ -42,6 +43,7 @@ private void onChunkData(ChunkDataS2CPacket packet, CallbackInfo ci) { @Inject(method = "onGameJoin", at = @At(value = "TAIL")) public void onGameJoin(GameJoinS2CPacket packet, CallbackInfo ci) { newDimension(new HashedSeedData(packet.commonPlayerSpawnInfo().seed()), false); + tryDatabase(); var preloaded = StructureSave.loadStructures(); if (!preloaded.isEmpty()) { Log.warn("foundRestorableStructures", preloaded.size()); @@ -51,9 +53,11 @@ public void onGameJoin(GameJoinS2CPacket packet, CallbackInfo ci) { @Inject(method = "onPlayerRespawn", at = @At(value = "TAIL")) public void onPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci) { newDimension(new HashedSeedData(packet.commonPlayerSpawnInfo().seed()), true); + tryDatabase(); } - public void newDimension(HashedSeedData hashedSeedData, boolean dimensionChange) { + @Unique + private void newDimension(HashedSeedData hashedSeedData, boolean dimensionChange) { DimensionType dimension = MinecraftClient.getInstance().world.getDimension(); ReloadFinders.reloadHeight(dimension.minY(), dimension.minY() + dimension.logicalHeight()); @@ -65,4 +69,12 @@ public void newDimension(HashedSeedData hashedSeedData, boolean dimensionChange) } } + @Unique + private void tryDatabase() { + Long seed = Database.getSeed(this.getConnection().getAddress().toString(), SeedCracker.get().getDataStorage().hashedSeedData.getHashedSeed()); + if (seed == null) { + return; + } + Log.printSeed("tmachine.foundWorldSeedFromDatabase", seed); + } } diff --git a/src/main/java/kaptainwutax/seedcrackerX/util/Database.java b/src/main/java/kaptainwutax/seedcrackerX/util/Database.java index 71421665..18dfa20d 100644 --- a/src/main/java/kaptainwutax/seedcrackerX/util/Database.java +++ b/src/main/java/kaptainwutax/seedcrackerX/util/Database.java @@ -8,17 +8,32 @@ import kaptainwutax.seedcrackerX.config.Config; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpStatus; +import org.apache.http.entity.ContentType; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class Database { + private static final String DATABASE_POST_URL = "https://script.google.com/macros/s/AKfycbye87L-fEYq2EkgczvhKb_kGecp5wL1oX95vg45TRSwNvpv7K-53zoInGTeI1FZ0kv7DA/exec"; + private static final String DATABASE_URL = "https://docs.google.com/spreadsheets/d/1tuQiE-0leW88em9OHbZnH-RFNhVqgoHhIt9WQbeqqWw/export?format=csv&gid=0"; // script link is not updated, this works fine + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); + private static final Duration TIMEOUT = Duration.ofSeconds(10); + private static final String SEEDCRACKERX_USER_AGENT = "SeedcrackerX mod"; + + private static final Map connectionToSeed = new HashMap<>(); + private static final Map hashedSeedToSeed = new HashMap<>(); + public static Text joinFakeServerForAuth() { try { MinecraftClient client = MinecraftClient.getInstance(); @@ -39,10 +54,15 @@ public static Text joinFakeServerForAuth() { return null; } + public static @Nullable Long getSeed(String connection, long hashedSeed) { + Long seed = connectionToSeed.get(connection); + if (seed != null) { + return seed; + } + return hashedSeedToSeed.get(hashedSeed); + } + public static void handleDatabaseCall(Long seed) { - HttpClient httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); MinecraftClient client = MinecraftClient.getInstance(); Map data = new HashMap<>(); data.put("serverIp", client.getNetworkHandler().getConnection().getAddress().toString()); @@ -52,17 +72,16 @@ public static void handleDatabaseCall(Long seed) { data.put("username", client.player.getName().getString()); data.put("hash", Config.get().anonymusSubmits? 1 : 0); - - HttpRequest request = HttpRequest.newBuilder() - .POST(HttpRequest.BodyPublishers.ofString(HttpAuthenticationService.buildQuery(data))) - .uri(URI.create("https://script.google.com/macros/s/AKfycbye87L-fEYq2EkgczvhKb_kGecp5wL1oX95vg45TRSwNvpv7K-53zoInGTeI1FZ0kv7DA/exec")) - .setHeader("User-Agent", "SeedcrackerX mod") - .header("Content-Type", "application/x-www-form-urlencoded") - .build(); + HttpRequest request = HttpRequest.newBuilder(URI.create(DATABASE_POST_URL)) + .timeout(TIMEOUT) + .POST(HttpRequest.BodyPublishers.ofString(HttpAuthenticationService.buildQuery(data))) + .setHeader(HttpHeaders.USER_AGENT, SEEDCRACKERX_USER_AGENT) + .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.getMimeType()) + .build(); try { - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() == 302) { //the page says "document moved" but the post gets processed + HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() == HttpStatus.SC_MOVED_TEMPORARILY) { //the page says "document moved" but the post gets processed Log.warn("database.success"); } else { Log.warn("database.fail"); @@ -71,4 +90,30 @@ public static void handleDatabaseCall(Long seed) { Log.warn("database.fail"); } } + + public static void fetchSeeds() { + HttpRequest request = HttpRequest.newBuilder(URI.create(DATABASE_URL)) + .timeout(TIMEOUT) + .GET() + .setHeader(HttpHeaders.USER_AGENT, SEEDCRACKERX_USER_AGENT) + .build(); + HTTP_CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .thenAccept(Database::parseCsv); + } + + private static void parseCsv(String csv) { + Arrays.stream(csv.split("\n")).skip(1).forEach(row -> { + try { + String[] seedEntry = row.split(","); + String connection = seedEntry[0]; + String seedString = seedEntry[2]; + long seed = Long.parseLong(seedString.substring(0, seedString.length() - 1)); + connectionToSeed.put(connection, seed); + long hashedSeed = Long.parseLong(seedEntry[6]); + hashedSeedToSeed.put(hashedSeed, seed); + } catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) { + } + }); + } } diff --git a/src/main/resources/assets/seedcrackerx/lang/en_us.json b/src/main/resources/assets/seedcrackerx/lang/en_us.json index 0f2afd7b..b420e5d4 100644 --- a/src/main/resources/assets/seedcrackerx/lang/en_us.json +++ b/src/main/resources/assets/seedcrackerx/lang/en_us.json @@ -65,6 +65,7 @@ "tmachine.decoratorWorldSeedSearch": "Looking for world seeds with decorators...", "tmachine.hashedSeedWorldSeedSearch": "Looking for world seeds with hashed seed...", "tmachine.foundWorldSeed": "Found world seed ${SEED}.", + "tmachine.foundWorldSeedFromDatabase": "Found world seed ${SEED} from database.", "tmachine.worldSeedSearchFinished": "Finished searching for world seeds.", "tmachine.noResultsRevertingToBiomes": "Finished search with no results, reverting back to biomes.", "tmachine.moreBiomesNeeded": "You need to collect more biome information",