Skip to content

Instantly share code, notes, and snippets.

@jtrussell
Last active August 21, 2024 16:16
Show Gist options
  • Save jtrussell/7539395bfc7b84888425b2f3c9c97ef7 to your computer and use it in GitHub Desktop.
Save jtrussell/7539395bfc7b84888425b2f3c9c97ef7 to your computer and use it in GitHub Desktop.
TCO Tracker Lite
// ==UserScript==
// @name TCO Tracker Lite
// @namespace http://tampermonkey.net/
// @version 0.2
// @description Track your game states in TCO
// @author jtrussell
// @match https://thecrucible.online
// @match https://thecrucible.online/*
// @grant none
// ==/UserScript==
;(function () {
'use strict'
/**
* Flag to indicate whether or note we're currently tracking
*/
let isTracking = false
/**
* The TCO Redux store
*/
let theStore = null
// The socket.io instance that we'll be listening to
let theSocket = null
/**
* Grab the state of the game when tracking begins
*
* We may start tracking after the start of the game, when this happens our
* "updates" will be incomplete.
* */
const initialGameState = {}
/**
* All game state events that come down the socket
*
* We'll ignore events that contain only log updates
*/
const gameEvents = []
window.SLW = window.SLW || {}
window.SLW.tcoTrackedInfo = gameEvents
const handleSocketEvent = (event) => {
const { messages, ...gameStateEvent } = event
if (Object.keys(gameStateEvent).length) {
gameEvents.push(gameStateEvent)
}
}
const isInGame = () => !!document.querySelector('.player-home-row-container')
const isGameOver = () =>
document
.querySelector('.messages.panel')
?.textContent?.includes('has won the game')
const initStore = () => {
const el = document.querySelector('.gravatar')
const key = Object.keys(el).find((k) => /__reactInternalInstance/.test(k))
let target = el[key]
let store
while (target.return) {
if (target.memoizedProps && target.memoizedProps.store) {
store = target.memoizedProps.store
break
}
target = target.return
}
theStore = store
window.SLW.theStore = theStore
}
const getSocketAsync = () => {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
try {
const state = theStore?.getState()
if (state?.games?.socket) {
clearInterval(interval)
const socket = state.games.socket
if (theSocket && socket !== theSocket) {
console.log('TCO Tracker Lite - Switching Game Sockets')
theSocket.off('gamestate', handleSocketEvent)
}
theSocket = socket
return resolve(socket)
} else {
return reject(new Error('Cannot find socket on state'))
}
} catch (e) {
reject(e)
}
}, 500)
})
}
const startupTracker = () => {
try {
initStore()
return getSocketAsync().then((socket) => {
if (!isTracking) {
isTracking = true
console.log(
'TCO Tracker Lite - Tracking Events',
gameEvents,
theStore,
theSocket
)
socket.on('gamestate', handleSocketEvent)
}
})
} catch (err) {
isTracking = false
theStore = null
theSocket = null
console.error('TCO Tracker Lite startup error')
console.error(err)
}
}
const shutdownTracker = () => {
isTracking = false
try {
initStore()
return getSocketAsync().then((socket) => {
if (isTracking) {
console.log('TCO Tracker Lite - Stopping Events')
socket.off('gamestate', handleSocketEvent)
}
})
} catch (err) {
theSocket?.off('gamestate', handleSocketEvent)
console.error('TCO Tracker Lite shutdown error')
console.error(err)
}
}
const ensureTrackerIsRunning = () => {
if (!isTracking) {
startupTracker()
}
}
const ensureTrackerIsNotRunning = () => {
if (isTracking) {
shutdownTracker()
}
}
const stopTrackerAndSubmitData = () => {
shutdownTracker().then(() => {
console.log('TCO Tracker Lite - Stopping Events')
console.log(gameEvents, theStore)
})
}
setInterval(() => {
if (isInGame()) {
if (!isGameOver()) {
ensureTrackerIsRunning()
} else if (isTracking) {
stopTrackerAndSubmitData()
}
} else {
ensureTrackerIsNotRunning()
}
}, 500)
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment