From adab3b7001426b298ab80c0722a49d8704fe1fc7 Mon Sep 17 00:00:00 2001 From: Andy Janata Date: Sun, 25 Feb 2018 22:01:21 -0800 Subject: [PATCH] Add card dealt metrics. --- .../java/net/socialgamer/cah/data/Game.java | 22 ++++++---- .../socialgamer/cah/metrics/KafkaMetrics.java | 43 +++++++++++++++---- .../net/socialgamer/cah/metrics/Metrics.java | 8 ++-- .../socialgamer/cah/metrics/NoOpMetrics.java | 16 ++++--- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/socialgamer/cah/data/Game.java b/src/main/java/net/socialgamer/cah/data/Game.java index 391b337..320b052 100644 --- a/src/main/java/net/socialgamer/cah/data/Game.java +++ b/src/main/java/net/socialgamer/cah/data/Game.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2012-2017, Andy Janata + * Copyright (c) 2012-2018, Andy Janata * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted @@ -41,6 +41,13 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; +import org.hibernate.Session; + +import com.google.inject.Inject; +import com.google.inject.Provider; + import net.socialgamer.cah.CahModule.UniqueId; import net.socialgamer.cah.Constants.BlackCardData; import net.socialgamer.cah.Constants.ErrorCode; @@ -59,13 +66,6 @@ import net.socialgamer.cah.data.QueuedMessage.MessageType; import net.socialgamer.cah.metrics.Metrics; import net.socialgamer.cah.task.SafeTimerTask; -import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; -import org.hibernate.Session; - -import com.google.inject.Inject; -import com.google.inject.Provider; - /** * Game data and logic class. Games are simple finite state machines, with 3 states that wait for @@ -178,6 +178,11 @@ public class Game { private final Provider cardcastServiceProvider; private final Provider uniqueIdProvider; private String currentUniqueId; + /** + * Sequence number of cards dealt. This allows re-shuffles and re-deals to still be tracked as + * unique card deals. + */ + private long dealSeq = 0; /** * Create a new game. @@ -799,6 +804,7 @@ public class Game { final WhiteCard card = getNextWhiteCard(); hand.add(card); newCards.add(card); + metrics.cardDealt(currentUniqueId, player.getUser().getSessionId(), card, dealSeq++); } sendCardsToPlayer(player, newCards); } diff --git a/src/main/java/net/socialgamer/cah/metrics/KafkaMetrics.java b/src/main/java/net/socialgamer/cah/metrics/KafkaMetrics.java index 3035004..f36e61c 100644 --- a/src/main/java/net/socialgamer/cah/metrics/KafkaMetrics.java +++ b/src/main/java/net/socialgamer/cah/metrics/KafkaMetrics.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017, Andy Janata + * Copyright (c) 2017-2018, Andy Janata * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted @@ -36,13 +36,6 @@ import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nullable; -import net.socialgamer.cah.data.BlackCard; -import net.socialgamer.cah.data.CardSet; -import net.socialgamer.cah.data.WhiteCard; -import net.socialgamer.cah.db.PyxBlackCard; -import net.socialgamer.cah.db.PyxCardSet; -import net.socialgamer.cah.db.PyxWhiteCard; - import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.KafkaProducer; @@ -61,6 +54,13 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import com.maxmind.geoip2.model.CityResponse; +import net.socialgamer.cah.data.BlackCard; +import net.socialgamer.cah.data.CardSet; +import net.socialgamer.cah.data.WhiteCard; +import net.socialgamer.cah.db.PyxBlackCard; +import net.socialgamer.cah.db.PyxCardSet; +import net.socialgamer.cah.db.PyxWhiteCard; + /** * Metrics implementation that sends all data to an Apache Kafka topic. @@ -70,7 +70,9 @@ import com.maxmind.geoip2.model.CityResponse; @Singleton public class KafkaMetrics implements Metrics { - private static final String metricsVersion = "0.1"; + // 0.1: initial version + // 0.2: added cardDealt + private static final String metricsVersion = "0.2"; private static final Logger LOG = Logger.getLogger(KafkaMetrics.class); private final ProducerCallback callback = new ProducerCallback(); @@ -367,4 +369,27 @@ public class KafkaMetrics implements Metrics { send(getEventMap("roundComplete", data)); } + + @Override + public void cardDealt(final String gameId, final String sessionId, final WhiteCard card, + final long dealSeq) { + trace("%s, %s, %s, %d", gameId, sessionId, card, dealSeq); + + final Map data = new HashMap<>(); + data.put("gameId", gameId); + data.put("sessionId", sessionId); + data.put("dealSeq", dealSeq); + + final Map whiteCardData = new HashMap<>(); + // same re: more custom deck sources + whiteCardData.put("isCustom", !(card instanceof PyxWhiteCard)); + whiteCardData.put("isWriteIn", card.isWriteIn()); + // negative IDs would be custom: either blank or cardcast. they are not stable. + whiteCardData.put("id", card.getId()); + whiteCardData.put("watermark", card.getWatermark()); + whiteCardData.put("text", card.getText()); + data.put("card", whiteCardData); + + send(getEventMap("cardDealt", data)); + } } diff --git a/src/main/java/net/socialgamer/cah/metrics/Metrics.java b/src/main/java/net/socialgamer/cah/metrics/Metrics.java index 03e8c9e..6335be2 100644 --- a/src/main/java/net/socialgamer/cah/metrics/Metrics.java +++ b/src/main/java/net/socialgamer/cah/metrics/Metrics.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017, Andy Janata + * Copyright (c) 2017-2018, Andy Janata * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted @@ -29,12 +29,12 @@ import java.util.Map; import javax.annotation.Nullable; +import com.maxmind.geoip2.model.CityResponse; + import net.socialgamer.cah.data.BlackCard; import net.socialgamer.cah.data.CardSet; import net.socialgamer.cah.data.WhiteCard; -import com.maxmind.geoip2.model.CityResponse; - /** * Collect metrics about card plays, and correlate them with (anonymized) user data. @@ -58,4 +58,6 @@ public interface Metrics { void gameStart(String gameId, Collection decks, int blanks, int maxPlayers, int scoreGoal, boolean hasPassword); + + void cardDealt(String gameId, String sessionId, WhiteCard card, long dealSeq); } diff --git a/src/main/java/net/socialgamer/cah/metrics/NoOpMetrics.java b/src/main/java/net/socialgamer/cah/metrics/NoOpMetrics.java index bb2b6c6..c00816e 100644 --- a/src/main/java/net/socialgamer/cah/metrics/NoOpMetrics.java +++ b/src/main/java/net/socialgamer/cah/metrics/NoOpMetrics.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017, Andy Janata + * Copyright (c) 2017-2018, Andy Janata * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted @@ -27,15 +27,15 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import net.socialgamer.cah.data.BlackCard; -import net.socialgamer.cah.data.CardSet; -import net.socialgamer.cah.data.WhiteCard; - import org.apache.log4j.Logger; import com.google.inject.Singleton; import com.maxmind.geoip2.model.CityResponse; +import net.socialgamer.cah.data.BlackCard; +import net.socialgamer.cah.data.CardSet; +import net.socialgamer.cah.data.WhiteCard; + /** * A no-op metrics implementation. All data are logged at TRACE then discarded. @@ -84,4 +84,10 @@ public class NoOpMetrics implements Metrics { LOG.trace(String.format("roundJudged(%s, %s, %s, %s, %s, %s)", gameId, roundId, judgeSessionId, winnerSessionId, blackCard, cards)); } + + @Override + public void cardDealt(final String gameId, final String sessionId, final WhiteCard card, + final long dealSeq) { + LOG.trace(String.format("cardDealt(%s, %s, %s, %d)", gameId, sessionId, card, dealSeq)); + } }