mirror of
https://github.com/jwetzell/psn-js.git
synced 2026-08-10 09:53:53 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
commonjs: true,
|
||||
es2021: true,
|
||||
},
|
||||
extends: ['airbnb', 'prettier'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
},
|
||||
rules: {
|
||||
'prefer-destructuring': 'off',
|
||||
'no-bitwise': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'import/extensions': 'off',
|
||||
'import/no-relative-packages': 'off',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
@@ -0,0 +1,2 @@
|
||||
*.md
|
||||
*.min.*
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
tabWidth: 2,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
bracketSameLine: true,
|
||||
printWidth: 120,
|
||||
};
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["esbenp.prettier-vscode"]
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true,
|
||||
"source.organizeImports": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# psn-js
|
||||
**!!DISCLAIMER!!**
|
||||
|
||||
This library is still a work-in-progress. I have gotten quick proof-of-concept working and complete implementation of the [PosiStageNet](https://github.com/vyv/psn-cpp/blob/master/doc/PosiStageNetprotocol_v2.03_2019_09_09.pdf) protocol is still being worked on. Please report anything you see that is not in compliance with the protocol.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
const dgram = require('dgram');
|
||||
const { Decoder } = require('.');
|
||||
|
||||
const client = dgram.createSocket('udp4');
|
||||
|
||||
const psnDecoder = new Decoder();
|
||||
|
||||
client.on('listening', () => {
|
||||
const address = client.address();
|
||||
client.setBroadcast(true);
|
||||
client.setMulticastTTL(128);
|
||||
client.addMembership('236.10.10.10');
|
||||
});
|
||||
|
||||
client.on('message', (message, remote) => {
|
||||
psnDecoder.decode(message);
|
||||
});
|
||||
|
||||
client.bind(56565, '0.0.0.0');
|
||||
@@ -0,0 +1,74 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
const { Parser } = require('binary-parser');
|
||||
const psnParser = require('./parsers/index.js');
|
||||
const { PSN_DATA_TRACKER_FIELD_PARSER } = require('./parsers/data/index.js');
|
||||
|
||||
const DATA_PACKET = 0x6755;
|
||||
const INFO_PACKET = 0x6756;
|
||||
|
||||
class Decoder {
|
||||
constructor() {
|
||||
this.info = {};
|
||||
|
||||
this.data = {
|
||||
trackers: {},
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(jwetzell): support multiple frame packets this might require some rework of how chunks are assumed
|
||||
decode(packetBuf) {
|
||||
const packet = psnParser(packetBuf);
|
||||
if (packet.id === INFO_PACKET) {
|
||||
if (packet.chunk_data?.tracker_list?.trackers) {
|
||||
packet.chunk_data?.tracker_list?.trackers.forEach((tracker) => {
|
||||
if (this.data.trackers[tracker.id] === undefined) {
|
||||
this.data.trackers[tracker.id] = {};
|
||||
}
|
||||
this.data.trackers[tracker.id] = {
|
||||
...this.data.trackers[tracker.id],
|
||||
id: tracker.id,
|
||||
name: tracker.tracker_name.tracker_name,
|
||||
};
|
||||
});
|
||||
}
|
||||
} else if (packet.id === DATA_PACKET) {
|
||||
if (packet.chunk_data?.tracker_list?.trackers) {
|
||||
packet.chunk_data?.tracker_list?.trackers.forEach((tracker) => {
|
||||
if (this.data.trackers[tracker.id] === undefined) {
|
||||
this.data.trackers[tracker.id] = {};
|
||||
}
|
||||
this.data.trackers[tracker.id] = {
|
||||
...this.data.trackers[tracker.id],
|
||||
id: tracker.id,
|
||||
};
|
||||
|
||||
if (tracker.data && tracker.data_len > 0) {
|
||||
const fields = new Parser()
|
||||
.array('fields', {
|
||||
type: PSN_DATA_TRACKER_FIELD_PARSER,
|
||||
lengthInBytes: tracker.data_len,
|
||||
})
|
||||
.parse(tracker.data);
|
||||
if (fields.fields) {
|
||||
fields.fields.forEach((field) => {
|
||||
if (field.data) {
|
||||
this.data.trackers[tracker.id] = {
|
||||
...this.data.trackers[tracker.id],
|
||||
...field.data,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
tracker.fields = fields.fields;
|
||||
delete tracker.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Decoder,
|
||||
};
|
||||
Generated
+2891
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "posistagenet",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"prettier": "^3.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"binary-parser": "^2.2.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
const Parser = require('binary-parser').Parser;
|
||||
|
||||
const PSN_PACKET_HEADER_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.uint64le('packet_timestamp')
|
||||
.uint8('version_high')
|
||||
.uint8('version_low')
|
||||
.uint8('frame_id')
|
||||
.uint8('frame_packet_count')
|
||||
.seek(4);
|
||||
|
||||
module.exports = {
|
||||
PSN_PACKET_HEADER_PARSER,
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
const { PSN_PACKET_HEADER_PARSER } = require('../common');
|
||||
const Parser = require('binary-parser').Parser;
|
||||
|
||||
const PSN_DATA_TRACKER_FIELD_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.choice('data', {
|
||||
tag: 'id',
|
||||
choices: {
|
||||
0x0000: new Parser().floatle('pos_x').floatle('pos_y').floatle('pos_z'),
|
||||
0x0001: new Parser().floatle('speed_x').floatle('speed_y').floatle('speed_z'),
|
||||
0x0002: new Parser().floatle('ori_x').floatle('ori_y').floatle('ori_z'),
|
||||
0x0003: new Parser().floatle('validity'),
|
||||
0x0004: new Parser().floatle('accel_x').floatle('accel_y').floatle('accel_z'),
|
||||
0x0005: new Parser().floatle('trgtpos_x').floatle('trgtpos_y').floatle('trgtpos_z'),
|
||||
0x0006: new Parser().uint64le('tracker_timestamp'),
|
||||
},
|
||||
});
|
||||
|
||||
const PSN_DATA_TRACKER_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.buffer('data', {
|
||||
length: 'data_len',
|
||||
});
|
||||
|
||||
const PSN_DATA_TRACKER_LIST_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.array('trackers', { type: PSN_DATA_TRACKER_PARSER, lengthInBytes: 'data_len' });
|
||||
|
||||
const PSN_DATA_CHUNK_DATA_PARSER = new Parser()
|
||||
.nest('packet_header', { type: PSN_PACKET_HEADER_PARSER })
|
||||
.nest('tracker_list', { type: PSN_DATA_TRACKER_LIST_PARSER });
|
||||
|
||||
module.exports = {
|
||||
PSN_DATA_CHUNK_DATA_PARSER,
|
||||
PSN_DATA_TRACKER_FIELD_PARSER,
|
||||
PSN_DATA_TRACKER_LIST_PARSER,
|
||||
PSN_DATA_TRACKER_PARSER,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
const { PSN_DATA_CHUNK_DATA_PARSER } = require('./data');
|
||||
const { PSN_INFO_CHUNK_DATA_PARSER } = require('./info');
|
||||
|
||||
const Parser = require('binary-parser').Parser;
|
||||
|
||||
const PSN_PACKET_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.choice('chunk_data', {
|
||||
tag: 'id',
|
||||
choices: {
|
||||
0x6755: PSN_DATA_CHUNK_DATA_PARSER,
|
||||
0x6756: PSN_INFO_CHUNK_DATA_PARSER,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = (buffer) => PSN_PACKET_PARSER.parse(buffer);
|
||||
@@ -0,0 +1,86 @@
|
||||
const { PSN_PACKET_HEADER_PARSER } = require('../common');
|
||||
|
||||
const Parser = require('binary-parser').Parser;
|
||||
|
||||
const PSN_INFO_SYSTEM_NAME_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.string('system_name', { length: 'data_len' });
|
||||
|
||||
const PSN_INFO_TRACKER_NAME_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.string('tracker_name', { length: 'data_len' });
|
||||
|
||||
const PSN_INFO_TRACKER_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.nest('tracker_name', {
|
||||
type: PSN_INFO_TRACKER_NAME_PARSER,
|
||||
});
|
||||
|
||||
const PSN_INFO_TRACKER_LIST_PARSER = new Parser()
|
||||
.uint16le('id')
|
||||
.uint16le('data_len', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return Number.parseInt(binary.substring(1), 2);
|
||||
},
|
||||
})
|
||||
.seek(-2)
|
||||
.uint16le('has_subchunks', {
|
||||
formatter: (item) => {
|
||||
const binary = item.toString(2).padStart(16, '0');
|
||||
return binary.charAt(0) === '1';
|
||||
},
|
||||
})
|
||||
.array('trackers', { type: PSN_INFO_TRACKER_PARSER, lengthInBytes: 'data_len' });
|
||||
|
||||
const PSN_INFO_CHUNK_DATA_PARSER = new Parser()
|
||||
.nest('packet_header', { type: PSN_PACKET_HEADER_PARSER })
|
||||
.nest('system_name', { type: PSN_INFO_SYSTEM_NAME_PARSER })
|
||||
.nest('tracker_list', { type: PSN_INFO_TRACKER_LIST_PARSER });
|
||||
|
||||
module.exports = {
|
||||
PSN_INFO_SYSTEM_NAME_PARSER,
|
||||
PSN_INFO_TRACKER_NAME_PARSER,
|
||||
PSN_INFO_TRACKER_PARSER,
|
||||
PSN_INFO_TRACKER_LIST_PARSER,
|
||||
PSN_INFO_CHUNK_DATA_PARSER,
|
||||
};
|
||||
Reference in New Issue
Block a user