first GodDaemons group commit
This commit is contained in:
@@ -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)
|
||||
},
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { methods as arenaMethods, construct as arenaConstruct } from './arenaHandlers.js'
|
||||
|
||||
export const afterLoginMethods = [
|
||||
arenaConstruct,
|
||||
]
|
||||
|
||||
export const meshActions = {
|
||||
...arenaMethods,
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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(),
|
||||
},
|
||||
} })
|
||||
},
|
||||
|
||||
}
|
||||
@@ -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(),
|
||||
} })
|
||||
},
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user