From 6e46109c9b6898cd03d5a73ebc9fce04e163ed0b Mon Sep 17 00:00:00 2001 From: magge <101107758+magge-faf@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:53:56 +0100 Subject: [PATCH] Enhanced ReplayDataParser with ModeratorEvent Support (#123) * Added ModeratorEvent to ReplayDataParser and handled activeCommandSource (#121) * WIP * Removed unused code * Removed unnecessary code and added basic unit testing for testParseModeratorEvent * Changed from Float to Integer, represents focus army * Switched to using Java records instead of traditional classes for less boilerplate code --- .../commons/replay/ModeratorEvent.java | 7 +++++ .../commons/replay/ReplayDataParser.java | 26 ++++++++++++++----- .../commons/replay/ReplayDataParserTest.java | 17 ++++++++++++ .../replay/TestModeratorEvents.fafreplay | 2 ++ 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 faf-commons-data/src/main/java/com/faforever/commons/replay/ModeratorEvent.java create mode 100644 faf-commons-data/src/test/resources/replay/TestModeratorEvents.fafreplay diff --git a/faf-commons-data/src/main/java/com/faforever/commons/replay/ModeratorEvent.java b/faf-commons-data/src/main/java/com/faforever/commons/replay/ModeratorEvent.java new file mode 100644 index 00000000..d0dce997 --- /dev/null +++ b/faf-commons-data/src/main/java/com/faforever/commons/replay/ModeratorEvent.java @@ -0,0 +1,7 @@ +package com.faforever.commons.replay; + +import java.time.Duration; + +public record ModeratorEvent(Duration time, String sender, String message, int activeCommandSource) { + +} diff --git a/faf-commons-data/src/main/java/com/faforever/commons/replay/ReplayDataParser.java b/faf-commons-data/src/main/java/com/faforever/commons/replay/ReplayDataParser.java index 3c992d20..b92d5dfb 100644 --- a/faf-commons-data/src/main/java/com/faforever/commons/replay/ReplayDataParser.java +++ b/faf-commons-data/src/main/java/com/faforever/commons/replay/ReplayDataParser.java @@ -20,12 +20,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -58,6 +53,8 @@ public class ReplayDataParser { @Getter private List chatMessages; @Getter + private List moderatorEvents; + @Getter private Map> commandsPerMinuteByPlayer; private float x; private float y; @@ -73,6 +70,7 @@ public ReplayDataParser(Path path, ObjectMapper objectMapper) throws IOException this.objectMapper = objectMapper; armies = new HashMap<>(); chatMessages = new ArrayList<>(); + moderatorEvents = new ArrayList<>(); commandsPerMinuteByPlayer = new HashMap<>(); parse(); } @@ -280,6 +278,10 @@ private void parseTicks(LittleEndianDataInputStream dataStream) throws IOExcepti parseGiveResourcesToPlayer((Map) lua); } + if (Objects.equals("ModeratorEvent", functionName)) { + parseModeratorEvent((Map) lua); + } + // No idea what this skips if (lua != null) { dataStream.skipBytes(4 * dataStream.readInt()); @@ -375,6 +377,18 @@ private void parseGiveResourcesToPlayer(Map lua) { } } + + void parseModeratorEvent(Map lua) { + String messageContent = (String) lua.get("Message"); + int fromInt = ((Number) lua.get("From")).intValue(); + int activeCommandSource = 0; // activeCommandSource is not available in .fafreplay, yet + if (lua.containsKey("activeCommandSource")) { + activeCommandSource = ((Number) lua.get("activeCommandSource")).intValue(); + } + moderatorEvents.add(new ModeratorEvent(tickToTime(ticks), Integer.toString(fromInt), messageContent, activeCommandSource)); + } + + private Duration tickToTime(int tick) { return Duration.ofSeconds(tick / 10); } diff --git a/faf-commons-data/src/test/java/com/faforever/commons/replay/ReplayDataParserTest.java b/faf-commons-data/src/test/java/com/faforever/commons/replay/ReplayDataParserTest.java index 4d292a82..c413d846 100644 --- a/faf-commons-data/src/test/java/com/faforever/commons/replay/ReplayDataParserTest.java +++ b/faf-commons-data/src/test/java/com/faforever/commons/replay/ReplayDataParserTest.java @@ -4,20 +4,24 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import com.google.common.io.LittleEndianDataInputStream; +import org.apache.commons.compress.compressors.CompressorException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.util.Arrays; import java.util.List; +import java.util.Objects; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; class ReplayDataParserTest { @@ -121,4 +125,17 @@ public void testLegacyFormat() throws Exception { byte[] reference = Files.readAllBytes(referenceFile); assertThat("Legacy compressed file matches reference", Arrays.equals(data, reference)); } + + @Test + public void testParseModeratorEvent() throws CompressorException, IOException { + Path replayFile = temporaryFolder.resolve("TestModeratorEvents.fafreplay"); + Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/replay/TestModeratorEvents.fafreplay")), replayFile); + + ReplayDataParser parser = new ReplayDataParser(replayFile, objectMapper); + + List moderatorEvents = parser.getModeratorEvents(); + ModeratorEvent firstEvent = moderatorEvents.getFirst(); + assertEquals(Duration.ofSeconds(20), firstEvent.time()); + assertEquals("Created a marker with the text: 'my fabelous marker test'", firstEvent.message()); + } } diff --git a/faf-commons-data/src/test/resources/replay/TestModeratorEvents.fafreplay b/faf-commons-data/src/test/resources/replay/TestModeratorEvents.fafreplay new file mode 100644 index 00000000..e927bb5b --- /dev/null +++ b/faf-commons-data/src/test/resources/replay/TestModeratorEvents.fafreplay @@ -0,0 +1,2 @@ +{"compression":null,"host":"magge","uid":22099630,"title":"FAF Develop test stuff","mapname":"scmp_039","options":null,"teams":{"2=[67103]":["magge"]},"complete":true,"recorder":"magge","state":"CLOSED","game_type":"UNKNOWN","featured_mod":"fafdevelop","max_players":4,"num_players":1,"sim_mods":{},"featured_mod_versions":{},"version_info":{"lobby":"dfaf-2024.3.0"},"game_end":1.710945031215E9,"time":0.0,"launched_at":1.71094495591E9} +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 \ No newline at end of file