first GodDaemons group commit

This commit is contained in:
STEINNI
2026-06-12 17:05:35 +00:00
commit 932b6e4752
20 changed files with 1655 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
export const construct = (redisCnx) => {
const tickMs = redisCnx.gpsSrv?.getGpsSettings().collisionTickMs ?? 100
setInterval(() => {
redisCnx.gpsSrv?.tickArena()
}, tickMs)
}
export const methods = {
handleAgentEvent(msg) {
const agentId = msg.sender
if(!agentId || typeof(agentId) !== 'string') {
console.warn(`[${this.redisId}] Agent event without sender`)
return
}
if(msg.eventType === 'change') {
const newVector = msg.payload?.newVector
if(!newVector || typeof(newVector.x) !== 'number' || typeof(newVector.y) !== 'number' || typeof(newVector.z) !== 'number') {
console.warn(`[${this.redisId}] Invalid newVector from ${agentId}`)
return
}
const newPosition = msg.payload?.newPosition ?? null
this.gpsSrv.onVectorChange(agentId, newVector, newPosition)
return
}
if(msg.eventType === 'remove') {
this.gpsSrv.onAgentRemove(agentId)
return
}
},
dispatchArenaMessage(msg, chan) {
const gps = this.config.gps
if(!gps || !this.gpsSrv) return false
if(this.matchesChan(chan, gps.agentVectorChangeChannel)) {
this.handleAgentEvent(msg)
return(true)
}
return(false)
},
}
+9
View File
@@ -0,0 +1,9 @@
import { methods as arenaMethods, construct as arenaConstruct } from './arenaHandlers.js'
export const afterLoginMethods = [
arenaConstruct,
]
export const meshActions = {
...arenaMethods,
}
+150
View File
@@ -0,0 +1,150 @@
const PROXIMITY_EPSILON = 1e-6
export function needsPrismRefresh(agent, now, prismTimeHeight, prismRefreshLeadSeconds = 0) {
return(now >= agent.since + prismTimeHeight - prismRefreshLeadSeconds)
}
export function advanceAgentSegment(agent, now) {
agent.position = positionAt(agent, now)
agent.since = now
agent.generation = (agent.generation ?? 0) + 1
}
export function positionAt(agent, t) {
const dt = t - agent.since
return({
x: agent.position.x + agent.vector.x * dt,
y: agent.position.y + agent.vector.y * dt,
z: agent.position.z + agent.vector.z * dt,
})
}
function distanceBetween(agentA, agentB, t) {
const a = positionAt(agentA, t)
const b = positionAt(agentB, t)
const dx = a.x - b.x
const dy = a.y - b.y
const dz = a.z - b.z
return(Math.sqrt(dx * dx + dy * dy + dz * dz))
}
export function buildPrism(agent, tStart, tEnd, nearMissDistance) {
const p0 = positionAt(agent, tStart)
const p1 = positionAt(agent, tEnd)
const pad = nearMissDistance
return({
xMin: Math.min(p0.x, p1.x) - pad,
xMax: Math.max(p0.x, p1.x) + pad,
yMin: Math.min(p0.y, p1.y) - pad,
yMax: Math.max(p0.y, p1.y) + pad,
zMin: Math.min(p0.z, p1.z) - pad,
zMax: Math.max(p0.z, p1.z) + pad,
tMin: tStart,
tMax: tEnd,
})
}
export function prismsIntersect(a, b) {
return(
a.xMin <= b.xMax && a.xMax >= b.xMin &&
a.yMin <= b.yMax && a.yMax >= b.yMin &&
a.zMin <= b.zMax && a.zMax >= b.zMin &&
a.tMin <= b.tMax && a.tMax >= b.tMin
)
}
export function minimalDistance(agentA, agentB, tStart, tEnd) {
const relPos = {
x: positionAt(agentA, tStart).x - positionAt(agentB, tStart).x,
y: positionAt(agentA, tStart).y - positionAt(agentB, tStart).y,
z: positionAt(agentA, tStart).z - positionAt(agentB, tStart).z,
}
const relVel = {
x: agentA.vector.x - agentB.vector.x,
y: agentA.vector.y - agentB.vector.y,
z: agentA.vector.z - agentB.vector.z,
}
const relVelSq = relVel.x * relVel.x + relVel.y * relVel.y + relVel.z * relVel.z
let tStar = tStart
if(relVelSq > 0) {
const dot = relPos.x * relVel.x + relPos.y * relVel.y + relPos.z * relVel.z
tStar = tStart - dot / relVelSq
}
tStar = Math.max(tStart, Math.min(tEnd, tStar))
return({
distance: distanceBetween(agentA, agentB, tStar),
time: tStar,
})
}
export function firstProximityEntry(agentA, agentB, tStart, tEnd, nearMissDistance) {
const d0 = distanceBetween(agentA, agentB, tStart)
if(d0 <= nearMissDistance + PROXIMITY_EPSILON) {
return({ time: tStart, distance: d0 })
}
const relPos = {
x: positionAt(agentA, tStart).x - positionAt(agentB, tStart).x,
y: positionAt(agentA, tStart).y - positionAt(agentB, tStart).y,
z: positionAt(agentA, tStart).z - positionAt(agentB, tStart).z,
}
const relVel = {
x: agentA.vector.x - agentB.vector.x,
y: agentA.vector.y - agentB.vector.y,
z: agentA.vector.z - agentB.vector.z,
}
const relVelSq = relVel.x * relVel.x + relVel.y * relVel.y + relVel.z * relVel.z
if(relVelSq === 0) return(null)
const a = relVelSq
const b = 2 * (relPos.x * relVel.x + relPos.y * relVel.y + relPos.z * relVel.z)
const c = relPos.x * relPos.x + relPos.y * relPos.y + relPos.z * relPos.z - nearMissDistance * nearMissDistance
const D = b * b - 4 * a * c
if(D < 0) return(null)
const sqrtD = Math.sqrt(D)
const maxDt = tEnd - tStart
const roots = [(-b - sqrtD) / (2 * a), (-b + sqrtD) / (2 * a)]
.filter(dt => dt >= 0 && dt <= maxDt)
.sort((x, y) => x - y)
for(const dt of roots) {
const t = tStart + dt
const d = distanceBetween(agentA, agentB, t)
if(d > nearMissDistance + PROXIMITY_EPSILON) continue
if(dt < 1e-12) return({ time: tStart, distance: d0 })
const dBefore = distanceBetween(agentA, agentB, t - 1e-6)
if(dBefore > nearMissDistance + PROXIMITY_EPSILON) return({ time: t, distance: d })
}
return(null)
}
export function compareWorldlinePair(agentA, agentB, prismTimeHeight, nearMissDistance, now) {
const tStart = Math.max(agentA.since, agentB.since, now)
const tEnd = Math.min(
agentA.since + prismTimeHeight,
agentB.since + prismTimeHeight
)
if(tEnd <= tStart) return(null)
const prismA = buildPrism(agentA, tStart, tEnd, nearMissDistance)
const prismB = buildPrism(agentB, tStart, tEnd, nearMissDistance)
if(!prismsIntersect(prismA, prismB)) return(null)
const min = minimalDistance(agentA, agentB, tStart, tEnd)
if(min.distance > nearMissDistance + PROXIMITY_EPSILON) return(null)
const entry = firstProximityEntry(agentA, agentB, tStart, tEnd, nearMissDistance)
if(!entry) return(null)
return({
agentA: agentA.id,
agentB: agentB.id,
time: entry.time,
distance: entry.distance,
minTime: min.time,
minDistance: min.distance,
})
}
+10
View File
@@ -0,0 +1,10 @@
import { methods as utilities, construct as utilitiesConstruct } from './utilities.js'
import { methods as positions } from './positions.js'
export const afterLoginMethods = [
utilitiesConstruct,
]
export const meshActions = {
...utilities,
...positions,
}
+146
View File
@@ -0,0 +1,146 @@
import { publishActionReply, parseAt } from '../../actionsHelper.js'
export const methods = {
/* Event-Rx:
{
"action": "GETAGENTPOSITION",
"reqid": "6az5e4r6a",
"payload": {
"agentId": "agent42",
"at": "2026-06-07T12:00:00.000Z"
}
}
Event-Tx:
{
"action": "GETAGENTPOSITION",
"success": true,
"reqid": "6az5e4r6a",
"payload": {
"agent": {
"id": "agent42",
"position": { "x": 1, "y": 2, "z": 3 },
"vector": { "x": 0, "y": 0, "z": 0 },
"since": 1717750800,
"generation": 2,
"at": "2026-06-07T12:00:00.000Z"
}
}
}
*/
async action_GETAGENTPOSITION(action, payload, reqid, sender, roles) {
const replyOpts = {
action,
reqid,
sender,
replyChannel: this.config.gps.gpsActionsReply,
}
if(!this.accessRights.canDo(roles, action)) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Unauthorized action !',
} })
return
}
const agentId = payload?.agentId
if(!agentId || typeof(agentId) !== 'string') {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Missing or invalid agentId',
} })
return
}
const at = parseAt(payload, () => this.gpsSrv.now())
if(at === null) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Invalid at timestamp',
} })
return
}
const agent = this.gpsSrv.getAgentPosition(agentId, at)
if(!agent) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: `Unknown agent: ${agentId}`,
} })
return
}
publishActionReply(this, { ...replyOpts, reply: {
success: true,
payload: { agent },
} })
},
/* Event-Rx:
{
"action": "GETAGENTSINPRISM",
"reqid": "6az5e4r6a",
"payload": {
"prism": {
"xMin": -10, "xMax": 10,
"yMin": -10, "yMax": 10,
"zMin": 0, "zMax": 5
},
"at": "2026-06-07T12:00:00.000Z"
}
}
Event-Tx:
{
"action": "GETAGENTSINPRISM",
"success": true,
"reqid": "6az5e4r6a",
"payload": {
"agents": [ ... ],
"at": "2026-06-07T12:00:00.000Z"
}
}
*/
async action_GETAGENTSINPRISM(action, payload, reqid, sender, roles) {
const replyOpts = {
action,
reqid,
sender,
replyChannel: this.config.gps.gpsActionsReply,
}
if(!this.accessRights.canDo(roles, action)) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Unauthorized action !',
} })
return
}
const prism = payload?.prism
if(!this.gpsSrv.isValidPrism(prism)) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Missing or invalid prism bounds',
} })
return
}
const at = parseAt(payload, () => this.gpsSrv.now())
if(at === null) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Invalid at timestamp',
} })
return
}
const agents = this.gpsSrv.getAgentsInPrism(prism, at)
publishActionReply(this, { ...replyOpts, reply: {
success: true,
payload: {
agents,
at: new Date(at * 1000).toISOString(),
},
} })
},
}
+109
View File
@@ -0,0 +1,109 @@
import { publishActionReply } from '../../actionsHelper.js'
export const construct = (redisCnx) => {
// console.log('Hello after login from utilities...')
// redisCnx.v42=0
// setInterval(redisCnx.move4243.bind(redisCnx), 200)
}
export const methods = {
/* Event-Rx:
{
"action": "TIME"
"reqid": "6az5e4r6a"
}
Event-Tx:
{
"action": "TIME",
"success": true,
"payload" : {
gpsTime: "2022-09-01T14:42:22.603Z",
redisTime: "2022-09-01T14:42:22.603Z"
},
"reqid": "6az5e4r6a"
}
*/
async action_TIME(action, payload, reqid, sender, roles){
publishActionReply(this, {
action,
reqid,
sender,
replyChannel: this.config.gps.gpsActionsReply,
reply: {
success: true,
payload: {
gpsTime: new Date().toISOString(),
redisTime: await this.redisClient.time(),
},
},
})
},
/* Event-Rx:
{
"action": "RELOADCONFIG"
"reqid": "6az5e4r6a"
}
Event-Tx:
{
"action": "RELOADCONFIG",
"success": true,
"reqid": "6az5e4r6a"
}
*/
async action_RELOADCONFIG(action, payload, reqid, sender, roles){
const replyOpts = {
action,
reqid,
sender,
replyChannel: this.config.gps.gpsActionsReply,
}
if(!this.accessRights.canDo(roles, action)) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Unauthorized action !',
} })
return
}
this.reloadAccessRights()
publishActionReply(this, { ...replyOpts, reply: {
success: true,
} })
},
/* Event-Rx:
{
"action": "GETCONFIG"
"reqid": "6az5e4r6a"
}
Event-Tx:
{
"action": "GETCONFIG",
"success": true,
"reqid": "6az5e4r6a",
payload: { ...the access rights, and roles... }
}
*/
async action_GETCONFIG(action, payload, reqid, sender, roles){
const replyOpts = {
action,
reqid,
sender,
replyChannel: this.config.gps.gpsActionsReply,
}
if(!this.accessRights.canDo(roles, action)) {
publishActionReply(this, { ...replyOpts, reply: {
success: false,
err: 'Unauthorized action !',
} })
return
}
publishActionReply(this, { ...replyOpts, reply: {
success: true,
payload: this.getAccessRights(),
} })
},
}