Skip to content

Commit

Permalink
Merge pull request #30 from MoT3rror/feature/interop-performance-boost
Browse files Browse the repository at this point in the history
Feature/interop performance boost
  • Loading branch information
MoT3rror authored Mar 21, 2021
2 parents 76bc379 + 365799c commit 5c385e9
Show file tree
Hide file tree
Showing 11 changed files with 6,452 additions and 3,252 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,5 @@ PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
DISCORD_BOT_TOKEN=
EI_API_HOST=http://localhost
EI_API_PORT=9000
15 changes: 12 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"name": "Launch EI CLI getPlayerInfo",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/js/egg-inc.js",
"args": ["getPlayerInfo", "--playerId", "457786961520754691"]
"program": "${workspaceFolder}/js/egg-inc-cli.js",
"args": ["getPlayerInfo", "--playerId", "EI6411720689451008"]
},
{
"type": "pwa-node",
"request": "launch",
"name": "Launch EI web server",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/js/egg-inc-web.js"
}
]
}
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ Shell to app server:

``vendor/bin/sail shell``

Update PHP libraries

``vendor/bin/sail composer install``

Apply migrations to DB

``vendor/bin/sail artisan migrate``

-----------

### Env config
Expand Down
48 changes: 13 additions & 35 deletions app/Api/EggInc.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@
use App\Exceptions\UserNotFoundException;
use Cache;
use Exception;
use Illuminate\Support\Facades\Http;
use Log;
use mikehaertl\shellcommand\Command;

class EggInc
{
public function getHttpClient()
{
return Http::baseUrl(config('services.egg_inc_api.url'));
}

public function getCoopInfo(string $contract, string $coop): \StdClass
{
$cacheKey = $contract . '-' . $coop;

return Cache::remember($cacheKey, 60 * 5, function () use ($contract, $coop) {
$appInfoCommand = new Command([
'command' => 'node ./js/egg-inc.js getCoopStatus --contract ' . $contract . ' --coop ' . $coop,
'procCwd' => base_path(),
]);

if (!$appInfoCommand->execute()) {
throw new Exception('Unable to get coop data');
}
$output = json_decode($appInfoCommand->getOutput());

$response = $this->getHttpClient()->get('getCoopStatus', ['contract' => $contract, 'coop' => $coop]);
$output = json_decode($response->body());
if (!$output) {
throw new CoopNotFoundException;
}
Expand All @@ -35,20 +33,9 @@ public function getCoopInfo(string $contract, string $coop): \StdClass

public function getCurrentContracts(): array
{
$contractCommand = new Command([
'command' => 'node ./js/egg-inc.js getAllActiveContracts',
'procCwd' => base_path(),
]);

$contracts = [];
if ($contractCommand->execute()) {
$contracts = json_decode($contractCommand->getOutput());
}

if (!$contracts) {
throw new \Exception('Could not load contracts');
}
return $contracts->contracts;
$response = $this->getHttpClient()->get('Periodicals');
$json = json_decode($response->body());
return $json->contracts->contracts;
}

public function getPlayerInfo(string $playerId): \StdClass
Expand All @@ -58,17 +45,8 @@ public function getPlayerInfo(string $playerId): \StdClass
}

return Cache::remember('egg-player-info-' . $playerId, 60 * 60 * 4, function () use ($playerId) {
$appInfoCommand = new Command([
// this might come back to hunt us but we will roll with it for now. Would require change to discord commands for lowercasing everything
'command' => 'node ./js/egg-inc.js getPlayerInfo --playerId ' . strtoupper($playerId),
'procCwd' => base_path(),
]);

if (!$appInfoCommand->execute()) {
throw new Exception('Unable to get player info');
}

$player = json_decode($appInfoCommand->getOutput());
$response = $this->getHttpClient()->get('getPlayerInfo', ['playerId' => strtoupper($playerId)]);
$player = json_decode($response->body());

if (!$player || !isset($player->approxTimestamp) || !$player->approxTimestamp) {
throw new UserNotFoundException('User not found');
Expand Down
3 changes: 3 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@
'client_secret' => env('DISCORD_CLIENT_SECRET'),
'redirect' => env('APP_URL') . '/login/discord/callback',
],
'egg_inc_api' => [
'url' => env('EI_API_HOST') . ':' . env('EI_API_PORT') . '/',
],
];
94 changes: 94 additions & 0 deletions js/egg-inc-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const b = require('base64-arraybuffer')
const axios = require('axios')
const protobuf = require("protobufjs")
const root = protobuf.loadSync('js/Proto/egginc.proto');

const ei_request = (path, payload, requestPB, responsePB) => {
return new Promise((resolve, reject) => {
var errMsg = requestPB.verify(payload);
if (errMsg)
throw Error(errMsg)

var buffer = requestPB.encode(requestPB.create(payload)).finish()

let options = {
url: `http://afx-2-dot-auxbrainhome.appspot.com/${path}`,
method: 'post',
data: 'data=' + b.encode(buffer),
}

axios(options).then((response) => {
let byteArray = new Array(0)
protobuf.util.base64.decode(response.data, byteArray, 0)

resolve(responsePB.decode(byteArray))
}).catch(err => {
reject(err);
})
})
}

class EggIncApi {
static getCoopStatus(contract, coop) {
const payload = {
contractId: contract,
code: coop
}

return ei_request(
'ei/coop_status',
payload,
root.lookupType('CoopStatusRequestPayload'),
root.lookupType('CoopStatusResponsePayload')
);
}

static getPeriodicals() {
const payload = {
userId: 'EI6411720689451008',
currentClientVersion: 30,
rinfo: {
eiUserId: 'EI6411720689451008',
clientVersion: 30,
version: '1.20.7',
platform: 'ANDROID'
}
}

return ei_request(
'ei/get_periodicals',
payload,
root.lookupType('GetPeriodicalsRequestPayload'),
root.lookupType('GetPeriodicalsResponsePayload')
);
}

static getPlayerInfo(playerId) {
var payload = {
clientVersion: 30,
platform: 2,
eiUserId: playerId,
username: '',
rinfo: {
eiUserId: playerId,
clientVersion: 30,
version: '1.20.7',
platform: 'ANDROID'
}
}

return ei_request(
'ei/first_contact',
payload,
root.lookupType('FirstContactRequestPayload'),
root.lookupType('FirstContactResponsePayload')
);
}

static getPlayerInfos(playerIds) {
const tasks = playerIds.map(playerId => this.getPlayerInfo(playerId));
return Promise.all(tasks);
}
}

module.exports = EggIncApi;
52 changes: 52 additions & 0 deletions js/egg-inc-cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const EggIncApi = require('./egg-inc-api');

// cli around the API
require('yargs')
.scriptName('Egg Inc API')
.usage('$0 <cmd> [args]')
.command('getAllActiveContracts', 'Get All Active Contracts', (yargs) => {}, (argv) => {
EggIncApi.getPeriodicals().then((data) => {
console.log(JSON.stringify(data.periodicals.contracts))
})
})
.command('getCoopStatus', 'Get Coop Status', (yargs) => {
yargs
.positional('contract', {type: 'string'})
.positional('coop', {type: 'string'})
}, (argv) => {
EggIncApi.getCoopStatus(argv.contract, argv.coop).then((data) => {
console.log(JSON.stringify(data.status))
})
})
.command('getPlayerInfo', 'Get Player Info', (yargs) => {
yargs
.positional('playerId', {type: 'string'})
}, (argv) => {
EggIncApi.getPlayerInfo(argv.playerId).then((data) => {
console.log(JSON.stringify(data.payload.data))
})
})
.command('getPlayerInfos', 'Get Player Infos', (yargs) => {
yargs
.positional('playerIds', {type: 'string'}) // comma separated list of ids
}, (argv) => {
EggIncApi.getPlayerInfos(argv.playerIds.split(',')).then((data) => {
console.log(JSON.stringify(data))
})
})
.command('events', 'Get Current Events', (yargs) => {}, (argv) => {
return EggIncApi.getPeriodicals().then((data) => {
console.log(JSON.stringify(data.periodicals.events))
})
})
.help()
.argv

/*
Usage examples:
node js/egg-inc-cli.js events
node js/egg-inc-cli.js getAllActiveContracts
node js/egg-inc-cli.js getCoopStatus --contract new-moon --coop dmv
node js/egg-inc-cli.js getPlayerInfo --playerId EI6411720689451008
node js/egg-inc-cli.js getPlayerInfos --playerIds EI6411720689451008,EI6411720689451008
*/
91 changes: 91 additions & 0 deletions js/egg-inc-web.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const express = require('express');
const bodyParser = require('body-parser');
const createError = require('http-errors');

require('dotenv').config();

const EggIncApi = require('./egg-inc-api');

// web server around the API
// setup express
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// define end points
app.get('/Periodicals', (req, res, next) => {
EggIncApi.getPeriodicals().then((data) => {
res.send(data.periodicals);
}).catch((error) => {
console.error(error);
return next(new createError.InternalServerError(error));
});
});

app.get('/getCoopStatus', (req, res, next) => {
EggIncApi.getCoopStatus(req.query.contract, req.query.coop).then((data) => {
res.send(data.status);
}).catch((error) => {
console.error(error);
return next(new createError.InternalServerError(error));
});
});

app.get('/getPlayerInfo', (req, res, next) => {
EggIncApi.getPlayerInfo(req.query.playerId).then((data) => {
res.send(data.payload.data);
}).catch((error) => {
console.error(error);
return next(new createError.InternalServerError(error));
});
});

app.get('/getPlayerInfos', (req, res, next) => {
// read input in various forms
let ids = [];
const { playerIds, playerIdsJoined} = req.query;
if (playerIds) { // individual ids
if (Array.isArray(playerIds)) {
ids = ids.concat(playerIds);
} else {
ids.push(playerIds);
}
}
if (playerIdsJoined) { // comma separated ids
ids = ids.concat(playerIdsJoined.split(','));
}
// run
EggIncApi.getPlayerInfos(ids).then((data) => {
res.send(data);
}).catch((error) => {
console.error(error);
//return next(error);
return next(new createError.InternalServerError(error));
});
});

app.get('/notFound', (/*req, res, next*/) => {
throw new createError.NotFound();
});

(async function () {
console.log('Starting!');

// start web server
app.listen(process.env.EI_API_PORT, () => {
console.log(`App started, port: ${process.env.EI_API_PORT}, running as: ${process.env.NODE_ENV}`);
});
})()

module.exports = app;

/*
Usage examples:
npm run start-ei-web
http://localhost:6001/Periodicals
http://localhost:6001/getCoopStatus?contract=new-moon&coop=dmv
http://localhost:6001/getPlayerInfo?playerId=EI6411720689451008
http://localhost:6001/getPlayerInfos?playerIdsJoined=EI6411720689451008,EI6411720689451008
http://localhost:6001/getPlayerInfos?playerIds[]=EI6411720689451008&playerIds[]=EI6411720689451008
http://localhost:6001/getPlayerInfos?playerIdsJoined=EI5426893308821504,EI6006465308917760,EI6622592762380288,EI6243749694275584,EI6670048183189504,EI5293412581900288,EI5889385330900992,EI4950801673355264
*/
Loading

0 comments on commit 5c385e9

Please sign in to comment.