package gtPlusPlus.api.analytics; import java.util.LinkedHashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Phaser; import com.mojang.authlib.GameProfile; import com.segment.analytics.Analytics; import gtPlusPlus.api.objects.Logger; import gtPlusPlus.core.lib.CORE; import gtPlusPlus.core.lib.LoadedMods; import gtPlusPlus.core.util.Utils; import gtPlusPlus.core.util.player.PlayerUtils; import gtPlusPlus.core.util.uuid.UUIDGenerator; import gtPlusPlus.core.util.uuid.UUIDUtils; import ic2.core.IC2; import net.minecraft.entity.player.EntityPlayer; public class SegmentAnalytics { //Globally Enabled public static boolean isEnabled = true; //Analytics Map with IDs public static final Map sAnalyticsMasterList = new ConcurrentHashMap(); //ID count private static int sAnalyticsMapID = 0; //Analytics Player Mapping public static final Map sAnalyticsToPlayermap = new ConcurrentHashMap(); //Set some Vars final BlockingFlush mBlockingFlush; final SegmentHelper mHelper; final UUIDGenerator mUuidGenerator; public final GameProfile mLocalProfile; public final String mLocalName; public final UUID mUUID; public final String mUserName; public final String mAnonymousId; protected Map mProperties = new LinkedHashMap<>(); final protected Phaser mPhaser; //Build a new instance of this class public SegmentAnalytics(EntityPlayer mPlayer){ LOG("Initializing Segment for "+mPlayer.getDisplayName()); //Give this Object an ID int currentID = sAnalyticsMapID; sAnalyticsMapID++; //Map this Object to it's ID and a Player UUID. sAnalyticsMasterList.put(currentID, this); sAnalyticsToPlayermap.put(mPlayer.getUniqueID(), currentID); //Create a Phaser this.mPhaser = new Phaser(1); //Set vars for player this.mLocalProfile = mPlayer.getGameProfile(); this.mLocalName = mLocalProfile.getName(); this.mUUID = PlayerUtils.getPlayersUUIDByName(mLocalName); this.mUserName = mUUID.toString(); this.mAnonymousId = getStringForm(generateIdForSession()); //Create a new UUID generator. this.mUuidGenerator = new UUIDGenerator(); //Use Segment Analytics instead of plain Google Analytics. this.mBlockingFlush = BlockingFlush.create(); this.mHelper = SegmentHelper.getInstance(); initTimer(mPlayer); } //Sets vars and stops Analytics running if the player profile is invalid. private boolean canProcess(){ //Invalid Player Profile if (mLocalProfile == null || !isEnabled){ return false; } if (mLocalName == null || mUUID == null || mUserName == null || mAnonymousId == null){ //LOG("One player var remained null, returning false."); return false; } if (mLocalName != null && mUUID != null && mUserName != null && mAnonymousId != null){ //LOG("All player vars are ok, returning true."); return true; } LOG("Something went wrong, returning false."); return false; } public void submitInitData(EntityPlayer mPlayer){ if (!canProcess()){ return; } mProperties = new LinkedHashMap<>(); mProperties.put("username", mLocalName); mProperties.put("gt_version", Utils.getGregtechVersionAsString()); if (LoadedMods.IndustrialCraft2){ mProperties.put("ic2_version", IC2.VERSION); } mProperties.put("country_code", CORE.USER_COUNTRY); mProperties.put("gtnh", CORE.GTNH); LOG("Created new Data packet, queued for submission."); //Old Code, now passed to Helper Class /*mHelper.enqueue(IdentifyMessage.builder() .userId(mUserName) //Save Username as UUID, for future sessions to attach to. .traits(mProperties) //.anonymousId(mAnonymousId) //Save Random Session UUID );*/ mHelper.addUser(this.mUserName, mProperties); if (CORE.GTNH){ mHelper.groupUser("GT:NewHorizons", this.mUserName); } else { mHelper.groupUser("GT:Vanilla", this.mUserName); } } public void submitTrackingData(String aActionPerformed){ submitTrackingData(aActionPerformed, null); } public void submitTrackingData(String aActionPerformed, Object aObject){ if (!canProcess()){ return; } Map properties = new LinkedHashMap<>(); properties.put("blockType", aObject); String mObjectAsString = "Unknown"; if (aObject != null){ mObjectAsString = aObject.toString(); } LOG("Queued submission of data for event "+aActionPerformed+". This was performed on "+mObjectAsString+"."); mHelper.trackUser(this.mUserName, aActionPerformed, properties); //Old Code, now passed to Helper Class /*mHelper.enqueue(TrackMessage.builder(aActionPerformed) // .userId(mUserName) // Save Username as UUID, for future sessions to attach to. .properties(mProperties) //Save Stats //.anonymousId(mAnonymousId) //Save Random Session UUID ); flushData(); */ } public void flushData(){ getAnalyticObject().flush(); } public void flushDataFinal(){ LOG("Flushing all data from Queue to Segment Analytics database."); getAnalyticObject().flush(); mBlockingFlush.block(); mPhaser.arriveAndAwaitAdvance(); getAnalyticObject().shutdown(); /*try { this.finalize(); } catch (Throwable e) { Utils.LOG_INFO("Could not finalize Analytics Object."); }*/ } public UUID generateIdForSession(){ return UUIDUtils.getUUIDFromBytes(generateUUID()); } private final byte[] generateUUID(){ byte[] mUUID; if (this.mUuidGenerator != null){ try { if ((mUUID = mUuidGenerator.next(4)) != null){ LOG("Generated Type 4 UUID for Session ID."); return mUUID; } else if ((mUUID = mUuidGenerator.next(1)) != null){ LOG("Generated Type 1 UUID for Session ID."); return mUUID; } } catch (Throwable t){ t.printStackTrace(); } } LOG("Generated Type 3 UUID for Session ID."); return UUIDUtils.getBytesFromUUID(UUID.randomUUID()); } public final String getStringForm(UUID mID){ return mID.toString(); } // Non-Dev Comments public static void LOG(final String s) { if (CORE.DEBUG){ Logger.getLogger().info("[Analytics] "+s); } } public static SegmentAnalytics getAnalyticsForPlayer(EntityPlayer mPlayer){ try { if (mPlayer != null){ if (SegmentAnalytics.sAnalyticsToPlayermap.containsKey(mPlayer.getUniqueID())){ int ID = sAnalyticsToPlayermap.get(mPlayer.getUniqueID()); return SegmentAnalytics.sAnalyticsMasterList.get(ID); } else { LOG("Map does not contain Player."); } } else { LOG("Invalid Player."); } } catch (Throwable t){ t.printStackTrace(); } return null; } public final Analytics getAnalyticObject() { return mHelper.getAnalyticsClient(); } public final Map getPlayerProperties(){ return this.mProperties; } public Timer initTimer(EntityPlayer mPlayer) { Timer timer; timer = new Timer(); timer.schedule(new initPlayer(mPlayer), 2 * 1000); return timer; } //Timer Task for notifying the player. class initPlayer extends TimerTask { final EntityPlayer aPlayer; public initPlayer(EntityPlayer mPlayer) { this.aPlayer = mPlayer; } @Override public void run() { //Let us submit a doorknock to Segment to let them know who this is. submitInitData(aPlayer); } } }