Add RemoveDuplicateDrops tool
This commit is contained in:
122
src/main/java/tools/mapletools/RemoveDuplicateDrops.java
Normal file
122
src/main/java/tools/mapletools/RemoveDuplicateDrops.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package tools.mapletools;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This tool takes the large drop-data.sql file as input, and removes all duplicate drops.
|
||||
* A drop is considered to be a duplicate if it has the same dropperid/mobid & itemid as another drop.
|
||||
* The first drop encountered in the input is kept, and any subsequent duplicates are removed.
|
||||
* <p>
|
||||
* Note about duplicate drops: duplicate drops are supposed to exist.
|
||||
* For example, Zakum should be able to drop multiple Zakum helmets. This is vanilla MapleStory behavior.
|
||||
* <p>
|
||||
* This tool is mostly useful during the migration from the old scripts to the new scripts in Liquibase,
|
||||
* since the old scripts relied on a combination of unique database constraint on mobid+itemid
|
||||
* and the INSERT IGNORE feature of MySQL to prevent duplicates. The INSERT IGNORE would attempt to insert the drop,
|
||||
* and if there was a duplicate it would fail and thereby be skipped.
|
||||
* In the new scripts, the unique constraint has been removed (because duplicate drops should be allowed),
|
||||
* so all the (previously ignored) inserts would succeed, and we end up with a bunch of duplicates.
|
||||
*
|
||||
* @author Ponk
|
||||
*/
|
||||
public class RemoveDuplicateDrops {
|
||||
// Precondition: copy from src/main/resources/db/(...)drop-data.sql to tools/input/drop-data.sql
|
||||
private static final Path DROP_DATA_INPUT_FILE = ToolConstants.getInputFile("drop-data.sql");
|
||||
private static final Path DROP_DATA_OUTPUT_FILE = ToolConstants.getOutputFile("drop-data_no-duplicates.sql");
|
||||
private static final Path REMOVED_LINES_OUTPUT_FILE = ToolConstants.getOutputFile("drop-data_removed-lines.sql");
|
||||
private static final Pattern INSERT_VALUE_PATTERN = Pattern.compile(".*\\((?<mob>\\d+), (?<item>\\d+), \\d+, \\d+, \\d+, \\d+\\).*");
|
||||
|
||||
private record DropIdentifier(int mobId, int itemId) {
|
||||
}
|
||||
|
||||
private record ProcessingResult(List<String> retainedLines, List<String> removedLines) {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Instant start = Instant.now();
|
||||
|
||||
System.out.printf("Reading %s%n", DROP_DATA_INPUT_FILE);
|
||||
List<String> lines = readDropDataLines();
|
||||
System.out.printf("Read %d lines%n", lines.size());
|
||||
|
||||
System.out.println("Removing duplicate drops...");
|
||||
ProcessingResult processingResult = removeDuplicateDrops(lines);
|
||||
System.out.printf("Removed %d lines%n", processingResult.removedLines.size());
|
||||
|
||||
System.out.println("Writing output to " + DROP_DATA_OUTPUT_FILE);
|
||||
writeDropDataOutput(processingResult.retainedLines);
|
||||
System.out.println("Writing removed lines to " + REMOVED_LINES_OUTPUT_FILE);
|
||||
writeRemovedLinesOutput(processingResult.removedLines);
|
||||
|
||||
Duration totalDuration = Duration.between(start, Instant.now());
|
||||
System.out.printf("Done! Total elapsed time: %d%n", totalDuration.toMillis());
|
||||
}
|
||||
|
||||
private static List<String> readDropDataLines() {
|
||||
try {
|
||||
return Files.readAllLines(DROP_DATA_INPUT_FILE);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read input file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static ProcessingResult removeDuplicateDrops(List<String> lines) {
|
||||
Set<DropIdentifier> encounteredDrops = new HashSet<>();
|
||||
List<String> retainedLines = new ArrayList<>();
|
||||
List<String> removedLines = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
Optional<DropIdentifier> optDropIdentifier = parseDropIdentifier(line);
|
||||
if (optDropIdentifier.isEmpty()) {
|
||||
retainedLines.add(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
DropIdentifier dropIdentifier = optDropIdentifier.get();
|
||||
if (encounteredDrops.contains(dropIdentifier)) {
|
||||
removedLines.add(line);
|
||||
} else {
|
||||
encounteredDrops.add(dropIdentifier);
|
||||
retainedLines.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
return new ProcessingResult(retainedLines, removedLines);
|
||||
}
|
||||
|
||||
private static Optional<DropIdentifier> parseDropIdentifier(String line) {
|
||||
Matcher matcher = INSERT_VALUE_PATTERN.matcher(line);
|
||||
if (!matcher.matches()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
int mobId = Integer.parseInt(matcher.group("mob"));
|
||||
int itemId = Integer.parseInt(matcher.group("item"));
|
||||
return Optional.of(new DropIdentifier(mobId, itemId));
|
||||
}
|
||||
|
||||
private static void writeDropDataOutput(List<String> retainedLines) {
|
||||
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(DROP_DATA_OUTPUT_FILE))) {
|
||||
retainedLines.forEach(pw::println);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to write drop data output file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeRemovedLinesOutput(List<String> removedLines) {
|
||||
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(REMOVED_LINES_OUTPUT_FILE))) {
|
||||
removedLines.forEach(pw::println);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to write removed lines output file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user