mirror of
https://github.com/R2Northstar/NorthstarMasterServer
synced 2024-11-15 11:06:58 +01:00
oops forgot to commit
This commit is contained in:
parent
b073906510
commit
aaa1f2488e
@ -7,6 +7,33 @@ const asyncHttp = require( path.join( __dirname, "../shared/asynchttp.js" ) )
|
||||
module.exports = ( fastify, opts, done ) => {
|
||||
// exported routes
|
||||
|
||||
// POST /client/origin_auth
|
||||
// used to authenticate a user on northstar, so we know the person using their uid is really them
|
||||
// returns the user's northstar session token
|
||||
fastify.get( '/client/origin_auth',
|
||||
{
|
||||
schema: {
|
||||
querystring: {
|
||||
uid: { type: "string" },
|
||||
token: { type: "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
async ( request, reply ) => {
|
||||
let authResponse = await asyncHttp.request( {
|
||||
method: "GET",
|
||||
host: "https://r2-pc.stryder.respawn.com",
|
||||
port: 443,
|
||||
path: `/nucleus-oauth.php?qt=origin-requesttoken&type=server_token&code=${request.query.token}&forceTrial=0&proto=0&json=1&&env=production&userId=${parseInt(request.query.uid).toString(16).toUpperCase()}`
|
||||
} )
|
||||
|
||||
let authJson = JSON.parse( authResponse.toString() )
|
||||
|
||||
return {
|
||||
success: ( !!authResponse.length && !!authJson.hasOnlineAccess && authJson.storeUri.includes( "titanfall-2" ) )
|
||||
}
|
||||
})
|
||||
|
||||
// POST /client/auth_with_server
|
||||
// attempts to authenticate a client with a gameserver, so they can connect
|
||||
fastify.post( '/client/auth_with_server',
|
||||
|
@ -5,6 +5,8 @@ const asyncHttp = require( path.join( __dirname, "../shared/asynchttp.js" ) )
|
||||
const VERIFY_STRING = "I am a northstar server!"
|
||||
|
||||
module.exports = ( fastify, opts, done ) => {
|
||||
fastify.register( require( "fastify-multipart" ) )
|
||||
|
||||
// exported routes
|
||||
|
||||
// POST /server/add_server
|
||||
@ -27,6 +29,20 @@ module.exports = ( fastify, opts, done ) => {
|
||||
async ( request, reply ) => {
|
||||
// check server's verify endpoint on their auth server, make sure it's fine
|
||||
// in the future we could probably check the server's connect port too, with a c2s_connect packet or smth, but atm this is good enough
|
||||
|
||||
let hasValidModInfo = true
|
||||
let modInfo
|
||||
|
||||
if ( request.isMultipart() )
|
||||
{
|
||||
try
|
||||
{
|
||||
modInfo = JSON.parse( ( await ( await request.file() ).toBuffer() ).toString() )
|
||||
hasValidModInfo = Array.isArray( modInfo.Mods )
|
||||
}
|
||||
catch ( ex ) {}
|
||||
}
|
||||
|
||||
let authServerResponse = await asyncHttp.request( {
|
||||
method: "GET",
|
||||
host: request.ip,
|
||||
@ -37,7 +53,8 @@ module.exports = ( fastify, opts, done ) => {
|
||||
if ( !authServerResponse || authServerResponse.toString() != VERIFY_STRING )
|
||||
return { success: false }
|
||||
|
||||
let newServer = new GameServer( request.query.name, request.query.description, 0, request.query.maxPlayers, request.query.map, request.query.playlist, request.ip, request.query.port, request.query.authPort, request.query.password )
|
||||
let newServer = new GameServer( request.query.name, request.query.description, 0, request.query.maxPlayers, request.query.map, request.query.playlist, request.ip, request.query.port, request.query.authPort, request.query.password, modInfo )
|
||||
|
||||
AddGameServer( newServer )
|
||||
|
||||
return {
|
||||
|
@ -1,11 +1,19 @@
|
||||
const http = require( "http" )
|
||||
const https = require( "https")
|
||||
//const url = require( "url" )
|
||||
|
||||
// could probably convert this to support https too later, effort tho
|
||||
module.exports = {
|
||||
request: function request( params, postData = null ) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
let req = http.request( params, reqResult => {
|
||||
let lib = http
|
||||
if ( params.host.startsWith( "https://" ) )
|
||||
{
|
||||
params.host = params.host.replace( "https://", "" )
|
||||
lib = https
|
||||
}
|
||||
|
||||
let req = lib.request( params, reqResult => {
|
||||
if ( reqResult.statusCode < 200 || reqResult.statusCode >= 300)
|
||||
return reject()
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
const crypto = require( "crypto" )
|
||||
const crypto = require( 'crypto' )
|
||||
const pjson = require( './pjson' )
|
||||
|
||||
class GameServer
|
||||
{
|
||||
@ -15,26 +16,29 @@ class GameServer
|
||||
|
||||
// bool hasPassword
|
||||
// string password
|
||||
|
||||
// object modInfo
|
||||
// object pdiff
|
||||
|
||||
constructor( nameOrServer, description, playerCount, maxPlayers, map, playlist, ip, port, authPort, password = "" )
|
||||
constructor( nameOrServer, description, playerCount, maxPlayers, map, playlist, ip, port, authPort, password = "", modInfo = {} )
|
||||
{
|
||||
if ( nameOrServer instanceof( GameServer ) ) // copy constructor
|
||||
{
|
||||
this.lastHeartbeat = nameOrServer.lastHeartbeat
|
||||
|
||||
this.id = nameOrServer.id
|
||||
this.updateValues(nameOrServer.name, nameOrServer.description, nameOrServer.playerCount, nameOrServer.maxPlayers, nameOrServer.map, nameOrServer.playlist, nameOrServer.ip, nameOrServer.port, nameOrServer.authPort, nameOrServer.password)
|
||||
this.updateValues( nameOrServer.name, nameOrServer.description, nameOrServer.playerCount, nameOrServer.maxPlayers, nameOrServer.map, nameOrServer.playlist, nameOrServer.ip, nameOrServer.port, nameOrServer.authPort, nameOrServer.password, nameOrServer.modInfo, nameOrServer.pdiffs )
|
||||
}
|
||||
else // normal constructor
|
||||
{
|
||||
this.lastHeartbeat = Date.now()
|
||||
|
||||
this.id = crypto.randomBytes(16).toString("hex")
|
||||
this.updateValues(nameOrServer, description, playerCount, maxPlayers, map, playlist, ip, port, authPort, password)
|
||||
this.id = crypto.randomBytes(16).toString( "hex" )
|
||||
this.updateValues( nameOrServer, description, playerCount, maxPlayers, map, playlist, ip, port, authPort, password, modInfo, pdiffs )
|
||||
}
|
||||
}
|
||||
|
||||
updateValues( name, description, playerCount, maxPlayers, map, playlist, ip, port, authPort, password = "" )
|
||||
updateValues( name, description, playerCount, maxPlayers, map, playlist, ip, port, authPort, password, modInfo, pdiffs )
|
||||
{
|
||||
this.name = name
|
||||
this.description = description
|
||||
@ -48,11 +52,19 @@ class GameServer
|
||||
this.authPort = authPort
|
||||
|
||||
this.hasPassword = false
|
||||
if ( password != "" )
|
||||
|
||||
if ( !!password )
|
||||
{
|
||||
this.password = password
|
||||
this.hasPassword = true
|
||||
}
|
||||
|
||||
// restrict modinfo keys
|
||||
this.modInfo = { Mods:[] }
|
||||
for ( let mod of modInfo.Mods )
|
||||
this.modInfo.Mods.push( { Name: mod.Name || "", Version: mod.Version || "0.0.0" } )
|
||||
|
||||
this.pdiffs = pdiffs
|
||||
}
|
||||
}
|
||||
|
||||
|
437
shared/pjson.js
Normal file
437
shared/pjson.js
Normal file
@ -0,0 +1,437 @@
|
||||
const NATIVE_TYPES = {
|
||||
int: { size: 4,
|
||||
read: ( buf, idx ) => buf.readInt32LE( idx ),
|
||||
write: ( buf, value, idx ) => buf.writeInt32LE( value, idx )
|
||||
},
|
||||
float: { size: 4,
|
||||
read: ( buf, idx ) => buf.readFloatLE( idx ),
|
||||
write: ( buf, value, idx ) => buf.writeFloatLE( value, idx )
|
||||
},
|
||||
bool: {
|
||||
size: 1,
|
||||
read: ( buf, idx ) => !!buf.readUInt8( idx ),
|
||||
write: ( buf, value, idx ) => buf.writeUint8( value, idx )
|
||||
},
|
||||
string: {
|
||||
size: 1,
|
||||
nativeArrayType: true,
|
||||
read: ( buf, idx, length ) => buf.toString( 'ascii', idx, idx + length ),
|
||||
write: ( buf, value, idx, length ) => buf.write( value.padEnd( length, '\u0000' ), idx, length, 'ascii' )
|
||||
}
|
||||
}
|
||||
|
||||
const ENUM_START = "$ENUM_START"
|
||||
const ENUM_END = "$ENUM_END"
|
||||
|
||||
const STRUCT_START = "$STRUCT_START"
|
||||
const STRUCT_END = "$STRUCT_END"
|
||||
|
||||
const PDIFF_ENUM_ADD = "$ENUM_ADD"
|
||||
const PDIFF_PDEF_START = "$PROP_START"
|
||||
|
||||
// reads a .pdef file, outputs all keys/values in a json format we use for this module
|
||||
function ParseDefinition( pdef )
|
||||
{
|
||||
let ret = {
|
||||
structs: {},
|
||||
enums: {},
|
||||
|
||||
members: []
|
||||
}
|
||||
|
||||
// falsey if not in use
|
||||
let currentEnumName
|
||||
let currentStructName
|
||||
|
||||
// read each line
|
||||
for ( let line of pdef.split( '\n' ) )
|
||||
{
|
||||
// read types/names
|
||||
let split = line.trim().split( /\s+/g )
|
||||
let type = split[ 0 ]
|
||||
|
||||
// check if this is a comment line
|
||||
if ( type.includes( "//" ) || type.length == 0 )
|
||||
continue
|
||||
|
||||
// user-defined type keywords
|
||||
if ( type[ 0 ][ 0 ] == '$' )
|
||||
{
|
||||
if ( !currentEnumName && !currentStructName )
|
||||
{
|
||||
let name = split[ 1 ]
|
||||
if ( !name.match( /^\w+$/ ) )
|
||||
throw new Error( `unexpected characters in keyword ${type}` )
|
||||
|
||||
if ( type == ENUM_START )
|
||||
{
|
||||
currentEnumName = name
|
||||
ret.enums[ currentEnumName ] = []
|
||||
}
|
||||
else if ( type == STRUCT_START )
|
||||
{
|
||||
currentStructName = name
|
||||
ret.structs[ currentStructName ] = []
|
||||
}
|
||||
else
|
||||
throw Error( `encountered unknown keyword ${type}` )
|
||||
}
|
||||
else if ( type == ENUM_END && currentEnumName )
|
||||
currentEnumName = ""
|
||||
else if ( type == STRUCT_END && currentStructName )
|
||||
currentStructName = ""
|
||||
else
|
||||
throw Error( `encountered unknown case with keyword ${type}` )
|
||||
}
|
||||
// we're in an enum, so enum members
|
||||
else if ( currentEnumName )
|
||||
{
|
||||
if ( !type.match( /^\w+$/ ) )
|
||||
throw new Error( `unexpected characters in enum member` )
|
||||
|
||||
// enums only have member names, values are just their index in the enum
|
||||
ret.enums[ currentEnumName ].push( type )
|
||||
}
|
||||
// normal members/struct members
|
||||
else
|
||||
{
|
||||
let name = split[ 1 ]
|
||||
if ( !name.match( /^[\w\[\]\{\}]+$/ ) )
|
||||
throw new Error( `unexpected characters in member name` )
|
||||
|
||||
// preparse type name for checking
|
||||
let checkType = type
|
||||
|
||||
let isNativeArrayType = false
|
||||
let isArray = false
|
||||
if ( type.includes( '{' ) )
|
||||
{
|
||||
// native array types are literally only strings, which are defined with string{length}
|
||||
checkType = type.substr( 0, type.indexOf( '{' ) )
|
||||
isNativeArrayType = true
|
||||
}
|
||||
else if ( name.includes( '[' ) )
|
||||
isArray = true
|
||||
|
||||
// verify type stuff
|
||||
if ( !( checkType in NATIVE_TYPES || checkType in ret.structs || checkType in ret.enums ) )
|
||||
throw Error( `got unknown type ${checkType}` )
|
||||
else
|
||||
{
|
||||
let arrayLength = -1
|
||||
|
||||
let newMember = { type: checkType, name: name }
|
||||
|
||||
if ( isNativeArrayType )
|
||||
{
|
||||
if ( !( checkType in NATIVE_TYPES ) || !NATIVE_TYPES[ checkType ].nativeArrayType )
|
||||
throw Error( `type ${checkType} was accessed like a native array type, while it is not one` )
|
||||
|
||||
// pretty sure this can't be an enum and has to be a number? unsure
|
||||
newMember.nativeArraySize = parseInt( type.substr( type.indexOf( '{' ) + 1 ) )
|
||||
}
|
||||
else if ( isArray )
|
||||
{
|
||||
let bracketIndex = name.indexOf( '[' )
|
||||
newMember.name = name.substr( 0, bracketIndex )
|
||||
|
||||
let arraySize = name.substring( bracketIndex + 1, name.indexOf( ']' ) )
|
||||
|
||||
if ( arraySize in ret.enums )
|
||||
newMember.arraySize = arraySize // just use the enum name here, easier to just let people resolve this themselves
|
||||
else
|
||||
newMember.arraySize = parseInt( arraySize )
|
||||
}
|
||||
|
||||
if ( currentStructName )
|
||||
ret.structs[ currentStructName ].push( newMember )
|
||||
else
|
||||
ret.members.push( newMember )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
function ParseDefinitionDiff( pdiff )
|
||||
{
|
||||
let ret = {
|
||||
enumAdds: {},
|
||||
pdefString: "",
|
||||
pdef: {}
|
||||
}
|
||||
|
||||
let pdefIdx = -1
|
||||
let currentEnumAddName
|
||||
let lines = pdiff.split( '\n' )
|
||||
|
||||
// read each line
|
||||
for ( let i = 0; i < lines.length; i++ )
|
||||
{
|
||||
// read types/names
|
||||
let split = lines[ i ].trim().split( /\s+/g )
|
||||
let type = split[ 0 ]
|
||||
|
||||
// check if this is a comment line
|
||||
if ( type.includes( "//" ) || type.length == 0 )
|
||||
continue
|
||||
|
||||
if ( currentEnumAddName )
|
||||
{
|
||||
if ( type == ENUM_END )
|
||||
currentEnumAddName = ""
|
||||
else
|
||||
{
|
||||
if ( !type.match( /^\w+$/ ) )
|
||||
throw Error( `unexpected characters in enum member` )
|
||||
|
||||
ret.enumAdds[ currentEnumAddName ].push(type)
|
||||
}
|
||||
}
|
||||
else if ( type == PDIFF_ENUM_ADD )
|
||||
{
|
||||
currentEnumAddName = split[ 1 ]
|
||||
if ( !currentEnumAddName.match( /^\w+$/ ) )
|
||||
throw Error( `unexpected characters in enum name` )
|
||||
|
||||
ret.enumAdds[ currentEnumAddName ] = []
|
||||
}
|
||||
else if ( type == PDIFF_PDEF_START )
|
||||
{
|
||||
pdefIdx = i + 1
|
||||
break
|
||||
}
|
||||
else
|
||||
throw Error( `hit unexpected case` )
|
||||
}
|
||||
|
||||
if ( pdefIdx != -1 )
|
||||
{
|
||||
let pdef = lines.slice( pdefIdx ).join( '\n' )
|
||||
ret.pdefString = pdef
|
||||
|
||||
ret.pdef = ParseDefinition( pdef )
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
function GetMemberSize( member, parsedDef )
|
||||
{
|
||||
let multiplier = 1
|
||||
|
||||
if ( typeof( member.arraySize ) == "string" )
|
||||
member.arraySize = parsedDef.enums[ member.arraySize ].length
|
||||
|
||||
multiplier *= member.arraySize || 1
|
||||
|
||||
if ( member.type in NATIVE_TYPES )
|
||||
{
|
||||
if ( NATIVE_TYPES[ member.type ].nativeArrayType )
|
||||
multiplier *= member.nativeArraySize
|
||||
|
||||
return NATIVE_TYPES[ member.type ].size * multiplier
|
||||
}
|
||||
else if ( member.type in parsedDef.enums )
|
||||
return NATIVE_TYPES.int.size * multiplier
|
||||
else if ( member.type in parsedDef.structs )
|
||||
{
|
||||
let structSize = 0
|
||||
for ( let structMember of parsedDef.structs[ member.type ] )
|
||||
structSize += GetMemberSize( structMember, parsedDef )
|
||||
|
||||
return structSize * multiplier
|
||||
}
|
||||
else
|
||||
throw Error( `got unknown member type ${member.type}` )
|
||||
}
|
||||
|
||||
//function PdataToJson( pdata, pdef )
|
||||
//{
|
||||
// let ret = {}
|
||||
// let i = 0
|
||||
//
|
||||
// function recursiveReadPdata( struct, base = "" )
|
||||
// {
|
||||
// for ( let member of struct )
|
||||
// {
|
||||
// let arraySize = member.arraySize || 1
|
||||
// if ( typeof( arraySize ) == 'string' )
|
||||
// arraySize = pdef.enums[ member.arraySize ].length
|
||||
//
|
||||
// for ( let j = 0; j < arraySize; j++ )
|
||||
// {
|
||||
// let memberName = base + member.name
|
||||
//
|
||||
// if ( member.arraySize )
|
||||
// {
|
||||
// memberName += "["
|
||||
// if ( typeof( member.arraySize ) == 'string' )
|
||||
// memberName += pdef.enums[ member.arraySize ][ j ]
|
||||
// else
|
||||
// memberName += j
|
||||
//
|
||||
// memberName += "]"
|
||||
// }
|
||||
//
|
||||
// if ( member.type in NATIVE_TYPES )
|
||||
// {
|
||||
// ret[ memberName ] = NATIVE_TYPES[ member.type ].read( pdata, i, member.nativeArraySize )
|
||||
// i += NATIVE_TYPES[ member.type ].size * ( member.nativeArraySize || 1 )
|
||||
// }
|
||||
// else if ( member.type in pdef.enums )
|
||||
// ret[ memberName ] = pdef.enums[ member.type ][ pdata.readUInt8( i++ ) ] // enums are uint8s
|
||||
// else if ( member.type in pdef.structs )
|
||||
// recursiveReadPdata( pdef.structs[ member.type ], memberName + "." )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// recursiveReadPdata( pdef.members )
|
||||
//
|
||||
// return ret
|
||||
//}
|
||||
|
||||
function PdataToJson( pdata, pdef )
|
||||
{
|
||||
let ret = {}
|
||||
let i = 0
|
||||
|
||||
function recursiveReadPdata( struct, base )
|
||||
{
|
||||
for ( let member of struct )
|
||||
{
|
||||
let arraySize = member.arraySize || 1
|
||||
if ( typeof( arraySize ) == 'string' )
|
||||
arraySize = pdef.enums[ member.arraySize ].length
|
||||
|
||||
let retArray = []
|
||||
|
||||
for ( let j = 0; j < arraySize; j++ )
|
||||
{
|
||||
if ( member.type in NATIVE_TYPES )
|
||||
{
|
||||
retArray.push( NATIVE_TYPES[ member.type ].read( pdata, i, member.nativeArraySize ) )
|
||||
i += NATIVE_TYPES[ member.type ].size * ( member.nativeArraySize || 1 )
|
||||
}
|
||||
else if ( member.type in pdef.enums )
|
||||
retArray.push( pdef.enums[ member.type ][ pdata.readUInt8( i++ ) ] ) // enums are uint8s
|
||||
else if ( member.type in pdef.structs )
|
||||
{
|
||||
let newStruct = {}
|
||||
recursiveReadPdata( pdef.structs[ member.type ], newStruct )
|
||||
retArray.push( newStruct )
|
||||
}
|
||||
}
|
||||
|
||||
base[ member.name ] = { type: member.type, arraySize: member.arraySize, nativeArraySize: member.nativeArraySize, value: member.arraySize ? retArray : retArray[ 0 ] }
|
||||
}
|
||||
}
|
||||
|
||||
recursiveReadPdata( pdef.members, ret )
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
//function PdataJsonToBuffer( json, pdef )
|
||||
//{
|
||||
// // calc size
|
||||
// let size = 0
|
||||
// for ( let member of pdef.members )
|
||||
// size += GetMemberSize( member, pdef )
|
||||
//
|
||||
// let buf = Buffer.alloc( size )
|
||||
//
|
||||
// let i = 0
|
||||
// let currentKey = 0
|
||||
// let keys = Object.keys( json )
|
||||
//
|
||||
// function recursiveWritePdata( struct )
|
||||
// {
|
||||
// for ( let member of struct )
|
||||
// {
|
||||
// let arraySize = member.arraySize || 1
|
||||
// if ( typeof( arraySize ) == 'string' )
|
||||
// arraySize = pdef.enums[ arraySize ].length
|
||||
//
|
||||
// for ( let j = 0; j < arraySize; j++ )
|
||||
// {
|
||||
// if ( member.type in NATIVE_TYPES )
|
||||
// {
|
||||
// NATIVE_TYPES[ member.type ].write( buf, json[ keys[ currentKey++ ] ], i, member.nativeArraySize )
|
||||
// i += NATIVE_TYPES[ member.type ].size * ( member.nativeArraySize || 1 )
|
||||
// }
|
||||
// else if ( member.type in pdef.enums )
|
||||
// {
|
||||
// buf.writeUInt8( pdef.enums[ member.type ].indexOf( json[ keys[ currentKey++ ] ] ), i++ ) // enums are uint8s
|
||||
// }
|
||||
// else if ( member.type in pdef.structs )
|
||||
// recursiveWritePdata( pdef.structs[ member.type ] )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// recursiveWritePdata( pdef.members )
|
||||
// return buf
|
||||
//}
|
||||
|
||||
function PdataJsonToBuffer( json, pdef, pdata )
|
||||
{
|
||||
// calc size
|
||||
let size = 0
|
||||
for ( let member of pdef.members )
|
||||
size += GetMemberSize( member, pdef )
|
||||
|
||||
let buf = Buffer.alloc( size )
|
||||
|
||||
let i = 0
|
||||
let currentKey = 0
|
||||
let keys = Object.keys( json )
|
||||
|
||||
function recursiveWritePdata( struct )
|
||||
{
|
||||
for ( let memberName in struct )
|
||||
{
|
||||
let member = struct[ memberName ]
|
||||
|
||||
let arraySize = member.arraySize || 1
|
||||
if ( typeof( arraySize ) == 'string' )
|
||||
arraySize = pdef.enums[ arraySize ].length
|
||||
|
||||
for ( let j = 0; j < arraySize; j++ )
|
||||
{
|
||||
let val = member.value
|
||||
if ( Array.isArray( val ) )
|
||||
val = member.value[ j ]
|
||||
|
||||
if ( member.type in NATIVE_TYPES )
|
||||
{
|
||||
NATIVE_TYPES[ member.type ].write( buf, val, i, member.nativeArraySize )
|
||||
i += NATIVE_TYPES[ member.type ].size * ( member.nativeArraySize || 1 )
|
||||
}
|
||||
else if ( member.type in pdef.enums )
|
||||
{
|
||||
buf.writeUInt8( pdef.enums[ member.type ].indexOf( val ), i++ ) // enums are uint8s
|
||||
}
|
||||
else if ( member.type in pdef.structs )
|
||||
recursiveWritePdata( val )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recursiveWritePdata( json )
|
||||
return buf
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
NATIVE_TYPES: NATIVE_TYPES,
|
||||
|
||||
ParseDefinition: ParseDefinition,
|
||||
ParseDefinitionDiff: ParseDefinitionDiff,
|
||||
GetMemberSize: GetMemberSize,
|
||||
PdataToJson: PdataToJson,
|
||||
PdataJsonToBuffer: PdataJsonToBuffer,
|
||||
}
|
Loading…
Reference in New Issue
Block a user