-
-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4d1b886
commit fb12a84
Showing
15 changed files
with
562 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# @greenlight/platform | ||
|
||
This package acts like a bridge to the packages consolidated which should make it easier to interact with the API's. All the code is loaded in a seperate worker thread which makes it faster for use in electron. | ||
|
||
## Class: Platform | ||
|
||
### constructor():void | ||
|
||
### loadWorker():Promise<boolean> | ||
|
||
Returns true if the user is currently authenticated. false if not. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"name": "@greenlight/platform", | ||
"version": "1.0.0", | ||
"dependencies": { | ||
"@greenlight/authentication": "workspace:^", | ||
"@greenlight/logger": "workspace:^", | ||
"@greenlight/storeapi": "workspace:^", | ||
"@greenlight/webapi": "workspace:^", | ||
"@greenlight/xcloudapi": "workspace:^" | ||
}, | ||
"license": "MIT", | ||
"main": "dist/index", | ||
"scripts": { | ||
"start": "yarn build:deps && yarn build && DEBUG='*' node --inspect dist/bin/example.js", | ||
"build": "tsc --build", | ||
"build:deps": "yarn workspace @greenlight/logger build && yarn workspace @greenlight/authentication build && yarn workspace @greenlight/storeapi build && yarn workspace @greenlight/webapi build && yarn workspace @greenlight/xcloudapi build", | ||
"clean": "rm -rf dist/ && rm -rf *.tsbuildinfo", | ||
"test": "yarn build && mocha -r ../../node_modules/ts-node/register tests/**.ts tests/**/*.ts" | ||
}, | ||
"devDependencies": { | ||
"@types/chai": "^4", | ||
"@types/debug": "^4.1.7", | ||
"@types/mocha": "^10", | ||
"@types/node": "^20.9.0", | ||
"chai": "^4.3.6", | ||
"mocha": "^10.1.0", | ||
"ts-node": "^10.9.2", | ||
"typescript": "^5.3.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
import GreenlightPlatform from '../index' | ||
import Logger from '@greenlight/logger' | ||
|
||
class Cli { | ||
private _platform = new GreenlightPlatform() | ||
public logger = new Logger('GreenlightPlatform:Cli') | ||
|
||
constructor(){ | ||
this.logger.log('Greenlight Platform Cli') | ||
|
||
this._platform.loadWorker().then((authStatus) => { | ||
this.logger.log('Worker loaded, Authentication status:', authStatus === true ? 'Authenticated' : 'Unauthenticated') | ||
|
||
this._platform.api.Gamepass.getList('coming').then((titles) => { | ||
this.logger.log('Titles:', titles) | ||
|
||
this._platform.close() | ||
|
||
}).catch((error) => { | ||
this.logger.error('Error getting gamertag:', error) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
new Cli() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import GreenlightPlatform from '..' | ||
|
||
export default class Authentication { | ||
|
||
private _platform:GreenlightPlatform | ||
|
||
constructor(platform:GreenlightPlatform){ | ||
this._platform = platform | ||
} | ||
|
||
getGamertag() { | ||
return this._platform.sendMessage({ | ||
controller: 'Authentication', | ||
action: 'user.getGamertag' | ||
}) | ||
} | ||
|
||
|
||
getUserhash() { | ||
return this._platform.sendMessage({ | ||
controller: 'Authentication', | ||
action: 'user.getUserhash' | ||
}) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import GreenlightPlatform from '..' | ||
|
||
export interface SiglResponse { | ||
properties: { | ||
siglId: string, | ||
title: string, | ||
description: string, | ||
requiresShuffling: boolean, | ||
imageUrl: string | ||
}, | ||
productIds: string[] | ||
} | ||
|
||
export default class Gamepass { | ||
|
||
private _platform:GreenlightPlatform | ||
|
||
constructor(platform:GreenlightPlatform){ | ||
this._platform = platform | ||
} | ||
|
||
async getTitles() { | ||
return await this._platform.sendMessage({ | ||
controller: 'xCloudApi', | ||
action: 'getTitles' | ||
}) | ||
} | ||
|
||
async getList(type:string) { | ||
const titles = await (this._platform.sendMessage({ | ||
controller: 'xCloudApi', | ||
action: 'getList', | ||
params: [ | ||
type | ||
] | ||
}) as Promise<SiglResponse>) | ||
|
||
return await this.getProductIds(titles.productIds) | ||
} | ||
|
||
async getSigl(siglId:string) { | ||
return await this._platform.sendMessage({ | ||
controller: 'xCloudApi', | ||
action: 'getSigl', | ||
params: [ | ||
siglId | ||
] | ||
}) | ||
} | ||
|
||
async getProductIds(productIds:string[]) { | ||
const products = await this._platform.sendMessage({ | ||
controller: 'storeApi', | ||
action: 'getProductIds', | ||
params: [ | ||
productIds | ||
] | ||
}) | ||
|
||
if(products.length !== productIds.length){ | ||
await this._platform.sendMessage({ | ||
controller: 'storeApi', | ||
action: 'loadProductIds', | ||
params: [ | ||
productIds | ||
] | ||
}) | ||
|
||
const retProducts = await this._platform.sendMessage({ | ||
controller: 'storeApi', | ||
action: 'getProductIds', | ||
params: [ | ||
productIds | ||
] | ||
}) | ||
|
||
return await this.parseItems(retProducts) | ||
} else { | ||
return await this.parseItems(products) | ||
} | ||
} | ||
|
||
async parseItems(items:any[]){ | ||
const parsedItems = [] | ||
for(const item in items){ | ||
const parsedItem = await this._platform.sendMessage({ | ||
controller: 'storeApi', | ||
action: 'parseTitle', | ||
params: [ | ||
items[item] | ||
] | ||
}) | ||
parsedItems.push(parsedItem) | ||
} | ||
return parsedItems | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import Authentication from './authentication' | ||
import Gamepass from './gamepass' | ||
|
||
export default { | ||
Authentication: Authentication, | ||
Gamepass: Gamepass | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import worker from 'node:worker_threads' | ||
import Logger from '@greenlight/logger' | ||
|
||
import PlatformWorker, { WorkerMessage, WorkerMessageResponse } from './worker' | ||
|
||
import Authentication from './controllers/authentication' | ||
import Gamepass from './controllers/gamepass' | ||
|
||
export default class GreenlightPlatform { | ||
|
||
public logger:Logger = new Logger('GreenlightPlatform') | ||
private _mainChannel = new worker.MessageChannel() | ||
private _worker?:any | ||
private _platformWorker?:PlatformWorker | ||
|
||
public api = { | ||
Authentication: new Authentication(this), | ||
Gamepass: new Gamepass(this), | ||
} | ||
|
||
constructor() { | ||
if(! worker.isMainThread) | ||
this.logger = this.logger.extend('Worker') | ||
|
||
this.logger.log('constructor() Creating new GreenlightPlatform instance') | ||
} | ||
|
||
loadWorker() { | ||
return new Promise((resolve, reject) => { | ||
if(! worker.isMainThread) | ||
throw new Error('Cannot load worker in worker thread, need to be loaded in the main thread.') | ||
|
||
this._worker = new worker.Worker(__filename); | ||
this._worker.once('message', (msg:'ok'|'unauthenticated'|'error') => { | ||
(msg !== 'error') ? resolve(msg === 'ok' ? true : false) : reject(false) | ||
|
||
this._worker.on('message', (msg:string) => { | ||
this.logger.error('Worker message:', msg) | ||
}) | ||
}) | ||
this._worker.on('error', (error:any) => { | ||
console.log('Worker error:', error) | ||
reject(error) | ||
}) | ||
this._worker.on('exit', (exit:any) => { | ||
if(exit > 0){ | ||
this.logger.error('Worker exited with exit code:', exit) | ||
} else { | ||
this.logger.log('Worker exited normally with exit coce:', exit) | ||
} | ||
}) | ||
|
||
this._worker.postMessage({ port: this._mainChannel.port1 }, [this._mainChannel.port1]); | ||
this._mainChannel.port2.on('message', (value) => this.message(value)); | ||
}) | ||
} | ||
|
||
startWorker() { | ||
this.logger.log('startWorker() Starting worker thread') | ||
|
||
worker.parentPort?.once('message', (handler) => { | ||
try { | ||
this._platformWorker = new PlatformWorker(this, handler.port) | ||
this._platformWorker.once('ready', (result) => { | ||
if(result === true) | ||
worker.parentPort?.postMessage('ok'); | ||
else | ||
worker.parentPort?.postMessage('unauthenticated'); | ||
}) | ||
} catch (error) { | ||
worker.parentPort?.postMessage('error'); | ||
} | ||
}) | ||
} | ||
|
||
message(value:WorkerMessageResponse) { | ||
this.logger.log('message() Received message:', JSON.stringify(value)); | ||
|
||
if(value.requestId !== undefined){ | ||
const promise = this._messagePromiseQueue.get(value.requestId) | ||
if(promise){ | ||
this._messagePromiseQueue.delete(value.requestId) | ||
promise.resolve(value.data) | ||
} else { | ||
this.logger.error('message() Promise not found for requestId:', value.requestId) | ||
} | ||
} | ||
} | ||
|
||
_messagePromiseQueue = new Map<number, { resolve:Function, reject:Function }>() | ||
|
||
sendMessage(value:WorkerMessage):Promise<any> { | ||
return new Promise((resolve, reject) => { | ||
if(this._worker){ | ||
value.requestId = Math.floor(Math.random() * 1000) | ||
this.logger.log('sendMessage() Sending message to worker:', JSON.stringify(value)); | ||
this._mainChannel.port2.postMessage(value) | ||
|
||
this._messagePromiseQueue.set(value.requestId, { resolve, reject }) | ||
} | ||
}) | ||
} | ||
|
||
close() { | ||
this.logger.log('close() Open promises:', this._messagePromiseQueue.size, this._messagePromiseQueue); | ||
return this.sendMessage({ action: 'close' }) | ||
} | ||
} | ||
|
||
if(! worker.isMainThread){ | ||
// Bootstrap worker | ||
const Platform = new GreenlightPlatform() | ||
Platform.startWorker() | ||
} |
Oops, something went wrong.