diff --git a/src/ACTListener.js b/src/ACTListener.js
new file mode 100644
index 0000000..de89d63
--- /dev/null
+++ b/src/ACTListener.js
@@ -0,0 +1,43 @@
+const getHost = () => /HOST_PORT=(wss?:\/\/.+)/.exec(window.location.search)
+
+export default function listenToACT(callback) {
+ if (!getHost()) return listenOverlayPlugin(callback)
+ return listenActWebSocket(callback)
+}
+
+function listenActWebSocket(callback) {
+ const url = new URLSearchParams(window.location.search)
+ const wsUri = `${url.get("HOST_PORT")}BeforeLogLineRead` || undefined
+ const ws = new WebSocket(wsUri)
+ ws.onerror = () => ws.close()
+ ws.onclose = () =>
+ setTimeout(() => {
+ listenActWebSocket(callback)
+ }, 1000)
+ ws.onmessage = function(e, m) {
+ if (e.data === ".") return ws.send(".")
+
+ const obj = JSON.parse(e.data)
+ if (obj.msgtype === "SendCharName") {
+ return callback(obj.msg)
+ } else if (obj.msgtype === "Chat") {
+ return callback(...obj.msg.split("|"))
+ }
+ }
+
+ return () => {
+ ws.close()
+ }
+}
+
+function listenOverlayPlugin(callback) {
+ const listener = e => {
+ callback(...e.detail)
+ }
+
+ document.addEventListener("onLogLine", listener)
+
+ return () => {
+ document.removeEventListener("onLogLine", listener)
+ }
+}
diff --git a/src/ACTWebsocket.js b/src/ACTWebsocket.js
deleted file mode 100644
index d4c393e..0000000
--- a/src/ACTWebsocket.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const handleCodes = new Set([
- '00',
- '01',
- '02',
- '21',
- '22',
- '33'
-])
-
-export default function listenActWebSocket( callback ) {
- const url = new URLSearchParams(window.location.search)
- const wsUri = `${url.get("HOST_PORT")}BeforeLogLineRead` || undefined
- const ws = new WebSocket(wsUri)
- ws.onerror = () => ws.close()
- ws.onclose = () => setTimeout(() => { listenActWebSocket( callback ) }, 1000)
- ws.onmessage = function(e, m) {
- if (e.data === ".") return ws.send(".") //PING
-
- const obj = JSON.parse(e.data);
- if (obj.msgtype === "SendCharName") {
- return callback(obj.msg, null)
- } else if (obj.msgtype === "Chat") {
- const code = obj.msg.substring(0, 2) //first 2 numbers POG
-
- if (handleCodes.has(code)) return callback(obj.msg, code) //NetworkAbility or NetworkAoeAbility
- }
- }
-
- return ws
-}
diff --git a/src/Action.js b/src/Action.js
index d24d979..54a4a4a 100644
--- a/src/Action.js
+++ b/src/Action.js
@@ -1,10 +1,10 @@
-import React from 'react'
-import './css/Action.css'
+import React from "react"
+import "./css/Action.css"
const gcdOverrides = new Set([
15997, //standard step
15998, //technical step
- 15999,
+ 15999,
16000,
16001,
16002, //step actions
@@ -28,13 +28,13 @@ const ogcdOverrides = new Set([
export default function Action({ actionId, additionalClasses }) {
const [apiData, setApiData] = React.useState()
-
+
React.useEffect(() => {
let current = true
void (async () => {
- const data = await (
- await fetch(`https://xivapi.com/Action/${actionId}`, { mode: 'cors' })
- ).json()
+ const data = await (await fetch(`https://xivapi.com/Action/${actionId}`, {
+ mode: "cors"
+ })).json()
if (current) {
setApiData(data)
}
@@ -44,16 +44,21 @@ export default function Action({ actionId, additionalClasses }) {
current = false
}
}, [actionId])
-
+
if (apiData === undefined || !apiData.Icon) {
return null
}
-
+
return (
)
}
diff --git a/src/App.js b/src/App.js
index a8a8d8a..9bc1166 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,100 +1,93 @@
-import React from 'react'
-import listenActWebSocket from './ACTWebsocket'
-import './css/App.css'
-import Action from './Action'
-import RotationContainer from './Rotation'
-import ReactDOM from 'react-dom'
+import React from "react"
+import listenToACT from "./ACTListener"
+import "./css/App.css"
+import Action from "./Action"
+import RotationContainer from "./Rotation"
+import ReactDOM from "react-dom"
+
+const handleCodes = new Set(["00", "01", "02", "21", "22", "33"])
export default function App() {
- // NOTE: unlike class state, useState doesn't do object merging; instead, it directly holds values
const [actionList, setActionList] = React.useState([])
const [encounterList, setEncounterList] = React.useState([])
React.useEffect(() => {
-
- // These values are only used internally by the handler,
- // we don't need to notify React that they were updated,
- // or keep their updates synchronized with actionList updates.
- //
- // This means we don't have to keep them in State (or Reducer)!
let selfId
- let lastTimestamp = ''
+ let lastTimestamp = ""
let lastAction = -1
- let currentZone = 'Unknown'
+ let currentZone = "Unknown"
- // we need keys to persist for each push, even if we shorten the array later,
- // so we store the key with the action; can't just use array index due to CSS
let lastKey = 1
- // listenActWebSocket should be changed to return the websocket,
- // and this effect should return a function that disconnects the websocket
- //
- // like "return () => { ws.close() }"
- let ws = listenActWebSocket((data, code) => {
- const openNewEncounter = (timestamp) => {
+ let closeFn = listenToACT((...logSplit) => {
+ const openNewEncounter = () => {
setEncounterList(encounterList => {
- if(encounterList[0] && encounterList[0].rotation && encounterList[0].rotation.length <= 0) {
+ if (
+ encounterList[0] &&
+ encounterList[0].rotation &&
+ encounterList[0].rotation.length <= 0
+ ) {
encounterList.shift()
}
-
+
encounterList.unshift({
name: currentZone,
rotation: []
})
-
- return encounterList.slice(0,3)
+
+ return encounterList.slice(0, 3)
})
}
-
- if (data.charID) {
- selfId = data.charID
+
+ if (logSplit.length === 1 && logSplit[0].charID) {
+ selfId = logSplit[0].charID
openNewEncounter()
return
}
-
- switch(code) {
- case '00':
- const [, , refCode, , message] = data.split('|')
- if(refCode === '0038' && message === 'end') openNewEncounter()
+
+ const [
+ logCode,
+ logTimestamp,
+ logParameter1,
+ logParameter2,
+ logParameter3
+ ] = logSplit
+
+ if (!handleCodes.has(logCode)) return
+
+ switch (logCode) {
+ case "00":
+ if (logParameter1 === "0038" && logParameter3 === "end")
+ openNewEncounter()
return
- case '01':
- const [, , , zoneName] = data.split('|')
- currentZone = zoneName
+ case "01":
+ currentZone = logParameter2
return
- case '02':
- const [, , logCharIdHex] = data.split('|')
- selfId = parseInt(logCharIdHex, 16)
+ case "02":
+ selfId = parseInt(logParameter1, 16)
openNewEncounter()
return
- case '33':
- const [, , , controlCode] = data.split('|')
- if(controlCode === '40000012' || controlCode === '40000010') openNewEncounter()
+ case "33":
+ if (logParameter2 === "40000012" || logParameter2 === "40000010")
+ openNewEncounter()
return
default:
break
}
-
- //if it's not any of these it must be Network(AOE)Ability, the bulk of what we want to handle
if (selfId === undefined) return
- const [, logTimestamp, logCharIdHex, , logActionIdHex] = data.split('|')
+ if (parseInt(logParameter1, 16) !== selfId) return
- // microoptimization: since selfId updates way less often,
- // save selfId as data.charID.toString(16), that way you don't need to
- // parse logCharIdHex every time
- if (parseInt(logCharIdHex, 16) !== selfId) return
-
- // we do a mathematical comparison with action though so can't optimize this away
- const action = parseInt(logActionIdHex, 16)
+ const action = parseInt(logParameter3, 16)
if (
action <= 8 ||
(logTimestamp === lastTimestamp && action === lastAction)
)
return
-
- if((Date.now() - Date.parse(lastTimestamp)) > 120000) openNewEncounter()//last action > 120s ago
+
+ if (Date.now() - Date.parse(lastTimestamp) > 120000) openNewEncounter() //last action > 120s ago
lastTimestamp = logTimestamp
lastAction = action
@@ -102,48 +95,55 @@ export default function App() {
const key = (lastKey % 256) + 1
lastKey = key
+ // This is pretty silly but it's the neatest way to handle the updates going
+ // out at the same time, without finding some way to merge the action lists....
ReactDOM.unstable_batchedUpdates(() => {
setActionList(actionList => actionList.concat({ action, key }))
setEncounterList(encounterList => {
- if(!encounterList[0]) {
+ if (!encounterList[0]) {
encounterList[0] = {
name: currentZone,
rotation: []
}
}
-
- encounterList[0].rotation.push( action )
-
+
+ encounterList[0].rotation.push(action)
+
return encounterList
})
})
- // This _probably_ should be done as a separate React.useEffect instead,
- // which runs as an effect whenever the value of actionList changes.
- // The problem there is, it would have to detect whether the list grew
- // since the last time it was called, otherwise it'd react (heh) to its own
- // updates.
- //
- // Easier to pair it with the previous set.
setTimeout(() => {
setActionList(actionList => actionList.slice(1))
}, 10000)
})
-
- return () => { ws.close() }
+
+ return () => {
+ closeFn()
+ }
}, [])
return (
-