From 83fa228615c5df03ef04548e96cbb300ed77991d Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 8 Aug 2016 00:57:16 +0200 Subject: [PATCH] Update - Passed login phase - Extend player object - Fixed stdin bug - Decode auth_info on authentication, to get email and generate uid - Hash code generator for uid --- .greet | 6 + asset_digest | Bin 18623 -> 15260 bytes cfg.js | 6 +- src/cycle.js | 5 +- src/index.js | 26 ++-- src/packets/Envelopes.AuthTicket.js | 14 ++ .../Envelopes.ResponseEnvelope.Auth.js | 22 +++ src/packets/Envelopes.ResponseEnvelope.js | 4 +- src/packets/Responses.CheckAwardedBadges.js | 17 +++ .../Responses.DownloadRemoteConfigVersion.js | 6 +- src/packets/Responses.DownloadSettings.js | 30 +++- src/packets/Responses.GetAssetDigest.js | 9 +- src/packets/Responses.GetPlayer.js | 35 +++-- src/packets/Responses.GetPlayerProfile.js | 25 ++++ src/packets/Responses.ItemTemplates.js | 141 ++++++++++++++++++ src/packets/index.js | 7 +- src/player.js | 138 +++++++++++++++-- src/process.js | 7 +- src/utils.js | 29 +++- 19 files changed, 471 insertions(+), 56 deletions(-) create mode 100644 .greet create mode 100644 src/packets/Envelopes.AuthTicket.js create mode 100644 src/packets/Envelopes.ResponseEnvelope.Auth.js create mode 100644 src/packets/Responses.CheckAwardedBadges.js create mode 100644 src/packets/Responses.GetPlayerProfile.js create mode 100644 src/packets/Responses.ItemTemplates.js diff --git a/.greet b/.greet new file mode 100644 index 0000000..e80ceec --- /dev/null +++ b/.greet @@ -0,0 +1,6 @@ + ______ _____ _____ _____ + | ___ \ _ | __ \ _ | + | |_/ / | | | | \/ | | | ___ ___ _ ____ _____ _ __ + | __/| | | | | __| | | |/ __|/ _ \ '__\ \ / / _ \ '__| + | | \ \_/ / |_\ \ \_/ /\__ \ __/ | \ V / __/ | + \_| \___/ \____/\___/ |___/\___|_| \_/ \___|_| diff --git a/asset_digest b/asset_digest index 27fbb7f3da5b779aa40cbbc08e6fc12641b750eb..57f3afa9b92452a2747f4e78486bbe9afdd8f65c 100644 GIT binary patch delta 5899 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 delta 9262 zcmZWvc}&!6cMc$+=%*vHD58RhBH%W%ih|C7xLiK3D_6bldR6qguUD^*2#5=Uyr6&# z&aeyvvJCqS%aEMYpUGF!v}wNn(R@jplC(*i?~kTU)23;fK4%=pFKO-~8Rq=XdCz&y zbDsD7Y`j1D&acX_WvY>jxsN8NO=f<_75?$@Y+tw|frn^fv0fK4Z`xE}z}?fPM(V|n zJRGW@F7C=-Nh9>`yfK}sXq1}yHGcoqnnnw=_9jf5Y}^&2*R8reZL+L6NPX1LuP*X{ z<}A9z7A}4FJ7WqvxtyQ#N1tg#0gqBw0u6E5BE60VHTRWiQw_dSakyyqUakt~36<=Z zmriyXq?Tz@gD+$*&A-E+CFVX@Ugc39iVwnX$${uPmvdA0Q8ue;zuZi6@GEZSCZFlz zbNkMF!nMxV>#m=`SseUd`_};XsDK>-DfB6}=aur#)mtXoqUK4(fE8v$0 zaad~O&sUI4C-ty<+T_xz@rn6Mbv$=+9jO!k#!Plhn;g_bPM?2v#vQ+wfG-ZG=yjTc z(~Vrq9c)A5R$t%6uFyH`{8Fvh-;j#X29dq@KKCKV_@(%-rJ=cc-Ly&NUyotyru>|E z9w76&(^qcsN0ks@$V8bYcmRc)HZ^~(Qyw`p<2;dv>UAj+mF*%o(%AK~qXj2oG@Bp1^Hf9HrNB*JW;6$L+Dxrn&&lskt}#C(XilG>iJT z07Et^UCl2EF!F*7U*o!~H^jU%La&<-2lQ;(G|J;Vc9UI*G;>KL{(m87+GJIQvkY6v z&Xr&5?tMfQ<1|eV61bH5*W&cWU3aKMv*tvS>vRrxpXVCMR|a>fahS7Zsu9Sl{gVhR zDfBs9QeUxDuRFn3xDTQ3hd6>YVy|Z57yNk%SCEPcG;W6Xk5u8ooHv2wIu=SpH~{q` zgU860_ygIlojViFog0h`E^)Og4K!wuU8@FS#(bFzTDFBOs2KO)*by{R#N~hrI|Fzm zhbEG_Ba%N;C8>W2G$Ll6;Tz81=xvY)SB<`+m9SFO zbwFr+KKJYK6N(-$PiO%-{EX`LQO};u2xyqyRPo*Zs94RamIND91T!#q&}U!9x2N9b zTG-M=qF%R%dN;Fs&wYNoD?9o~>XO*YT&JD}1LSrTs+-}+8}u&pWP<^R=0-%#BXC~B z^~+XM<(y;p`T0ufOeQrQoSOlVed2cR^u?R@c&c%q1nXXc0%xDptjK6r9Jla`Y_5EY zI(cYdmb!?>E?$hJiVNXeGd!(hQH$mnwoR1*!qm&4R$pV- z?gVO1VZrKHy>5RnRm>9_a)1+FF64YZG%+?yRLl%Bf+=ZY(#NR?sIu-YKm)o#xc?NPD#Mj zzf4Npqyq?CzohYNetzHr+foC6$|dzTbJ53D^qKo*1bW?`oi1si6uP`(r{3es_qUQU7dGTjqiF>$rp)c+{8aSS|tCP^YkmTf!a*qRPC$C?30oAh`GO zul}nM>zK046O5q7qvY_p2Fg3LXao>JHUY4+c{o8fx9Nor*~O-*veRs*@s;>k1^&h$ zleQW$6#JgAauK%$Nk=&_w!(3)ffyRZ0or*oghsh$5r0w_f0MTj^Ue)JG==xnZ1N-X zO?alE*$CU|Ny;P=?xYcqgowdIE6&pBI&M~f`5Umd1fH!{$3X`1naD*K4K7_s zLmQ{cV%ef<=kLtqD%g#3t1s;1{91F)^I&UerC#^TBepGlg7Nkt;wRAGkL%WPo8YcY z{qKALyH3Msv&lG`Id$J7WID;i1vO|Yem9Tnb8hTphhH|x{mpUeLQ`8oyXO`d(f}2+ z!1NnLF-a!4qCnE-#;l+|Hlt;`qfzWS?jhUSQ|wfKwqR!#!hVbYfHH5k;;3pLPYOc$ zHAqOSUYvB3CP0uj!bp+U6@~Pg$4DjW^3sG(8bEI#eQ)SwU!4dF1X6@N(aKxba_1g? zo{^E4#5DyFSfiw>bh+7JgEABfUE{U%!?>+P9>h8RfcI1gmnw1I);I0w=bQ+>Hnc53kF)G!h+za!z0r2hQ`cmzSUvpmLvQ&T^ zZX!{$5A4#^{e9f(hejxA#*FO-ztw;sCP>AHV%nCYOUD@$%4Ur!E1~wld*N}_`1VIBJbMB70w+faUuLT10 z@Ah+DLJD+QyZYBqLk8EuF^x`r8+@y%IVcLYJS@Y$?c_3UeiHQPIx6*<-)CXeo2iQH z)!{`3=t=+wbHL{t&k8;z1T#d$M`i-Wmb7Q&_gG6#?aZe3&vohnl$VFn>qE2IOs(VMmHo-wpQnq?RzCmWA`KUZ^x z9Qlr&;O-vko|}6J!t|fR4C!p4UIhNZXUui@7Qg(8S<7<0E=laUZ71ld;@7-QT$Ot0 z9I5ZZbJLNsei|dY&z4%zM$S1x=@Xa+yHAm0?vd>)Pn-rQp!{lp&Z=KY^@kfXz)}-j zs$mGvv{?_iW2n~ybT@#0Z|zOcOIIM6afn)DxF&1?%G0DGBY-mpOr0_}y&2=@7p2#} z23-f&sBmW_yneiO`X_#?S;-R1FCJ=6^*92>a`JO(Ce+01d(9CUs#OBikt`U$rg0wC zs?&BX1!ApP-~XJu7fq`EAwq;a5Hk!*pE37^kv&IB-2)B15OiRL78gGfLO5bc>BSWC7z3Gn9na78Phm z2fP()t$HCs*FYO}DR{;{%|hcb)P59~ibie$f7Gdaiw!6mm}Z>c`ks-+-C)w;uX#5E zEXNYrd6b7QXt=hpZB@|eq^MwSQGZyB3zQvg06G09C7(ES?kH?+Tq05lDQn_}Io$gz zkDm>}S<5#>LdcNHS%Q*-*J(^Hg)>a%lKAO{~upfziaTe_602m0zgYQMnli!_4*k z?QYMO)nqR8q}&XNc8AO%ps~_h!K({)Wnui+;-6RiOkI!i)T<9S!56-K;jDYek}ZtX zGf%Gzpuv6IzK~1MIHQ@TdF07~&;Yip-$xm?iZpzSqG3pQ3pP)he5u$@B3($tKB$&P zRHL}-Y;*uzzodb)@ZCWwW2t0}S|Hc^eHn}XLG(JBlUu{Zz7E-`6vl0&0mI$ULyx#@ z4R;%OST!w0xhz0(uN>T)aB^NcwlP7(e4Xg6_x$GRwRJoK7frvV&TKI1M{;fC4=Nzq zunkkl1MrW&=#NBd6JTp;6JTvOsMNBXpFia0sYb1Ok>se3B-R^TqAo?t-H9r=m{4%< z@xgiV_m&n;kq%yF>(i)gv4LKiytlP~&XV5V^(RX8yo z;lJfq`!-Y8iTl6exy!hBKUrkafJvfX2EiyJ1eX#FSg&`8Z_7@8F7dbG-i9PXVpzCh z$Cv7S;1ak|OCC_m18fb{`(?&d7~8?Mc3%LRb^Y34k9%Zq7W9o-bMxffkX2_fHBH?5 zrFtHz|6T?eQ7!}5B8Bt%=Ds;1Kbnzzev5H}!jy4S@}8T?NRjA+6;R}pqvx50!mPgK% zzLiVDABwdFkWZ(cdzUq}@(wo~I|@W)FX75l{9GliFswt~yP=*ih`q6E)gc$trCYcb zvT+f&p1KEsJb32aure=ScbM!!H|l(18BokLcJZ zaXT}?)1Q#<0iO-IfUGy3j@O*nPVvMO>Icp{?_4=vSO@{u#T5&nNz^~Z8PjAkVPPn_ z@*Xt}vOfANlAd9%(MaI|&6xx2DK3pMa@z?InA*K6F9SR|3Q35Crq`0KvN3A@PP(-Y zyVM-beUaLE<80E#7nOukz6ISdnSNJa#AbqT5Vjm4nqbe-^cSuzqC!qxgnG~ zbOZ?6)GzUv5he{!L?`%+m*P4&zEq0T23TcN#cr@o14?6CdPK52dLahI$NuB*<56Ne zmLk}l`K)7Ov(tnk+c2Os>k)3f%iaDLgLViiY~1FjRp*e(T`l(%fNof>-`=1@PvUen z9-`=h`n#Z~o>{OW%SHdWA+wePOyxIm;?C8E9g;E=RY>GBCoTqKZPmK zf#yndQi*20jm7}Wen1Q$!bY`Vja(K$es&SQGbC5VL%To#r981fmTSXtnb|4SM^2*$ zpg(-aeE?>kx+BI;+?faDs^nc|25S>HC4LWR1#QJYA7=Zm^i*=4qls)I#OF_lTm9`C z!#4aWCHuf=XRmJ*b{?D9j>aMzh-4$e-zD6nHt0a^-51iX?7`0cBM+$T1l+GpREY_O zdy>~aY*`4#n1?faFd#JN$&j2P$d=DE{*>LuFw{+EnHB4{mITapCsc`&TzC_Ro$U_I ziUg7k(g3*Uk)IQk;?>3|WnB52Q zgcePL1a!x**b=r6-&}AQxl%u`#eA_KeC*eJ=8Hm4N$`M`8IWUl&iuIbse($2|0(&J zV+-my5u^TktpSTd+;po5{W24I^E_^!D^+U*H*o8LJ4vYQ%R~F4$*MUwV8@dO5=~Du zq$BMZ%GeD1Y=aT;dC?R6MphEFF*Lx>A5s5?Qw8S;$wk6(FK zvLa6yl|&DSwsAzT3-4HX`0mm(C_hvI2=%{`b5qciCgFO3$=h5=`w}*t6MH8HW%gyw zeJ$-?_QT*MziRG)WzfCqo5{uiei>l&R;Ko}+12|`y znp~HkjiAAz^<3}6WPB{T9T&n|TZ*bFzg z^a`_jleQuVPNBBp8FUg8#+H;2{t%s&zGD}*iACG7!+pEnkONA=8p_5EK0|Wjiq);* z4F}%J=-|BSK5F@;IugNYZo;M48c*h{yUFUuHR|8j<3{Q9__q2U@p$90A0+l6(InmL zfP<}1cd$K)2EW6n&(8DbNNh%b*noRc9iY=C#6DB&_dL1wx8hrfWK%eqvv)`Ar?wD& zra2E%+9O;+|Zmc6@$X5R=4q1y8PagtgO6!F`=TTt9%XbyW&_o1TMvu<>i={=>#V+%-z5eJ>CAZzoj-W6z$cLNMlK zpa6cIwNp;)*z!PcN+^vQ;K_8dntCMn#^=9o{=+k8MBvOf+|H8X-zn9JT^85mNc8^qq zK3^8dRZud|d=@ZoJ;RjvL&j81F9o~CCr%3LLj8`+f1#NA#bkCH*fJ(%=Q;gdg*4P~m}&q)t3@w@W( zlL}4A#hYH3f$;IznKAY12Ae&)w*s%w% z>cQRVZgA!{J@yoMe-0ry{2&7tlX`Y5L#i&$2Z(YDk2u+8Kc*^cN?@h zW*4csX*jY2i`+WGHOy<97shq-abRjkb;B^PkgA3P{s&0kzBPNIN$~t)X5lQP%)W>i21RnQTYn6=SZ>l;|jm>*+<1 zz+fW69^(29$MU{Ifoqq}#$v5;8MmaMPFvHVVjLJ+c!JL{b=>2cJ%Va}0rEP0_XOb4 zqf2*%W`OWX1O?+Q8DwV2zH zI|~7O4!Ph5W?;hB!Frg#y_*GKlJ9bj9NURwf2pS4b6*JcBnws;t@3y3S98D3z?gu!81>v?{dwBH{S1p$8LIc#PzsitJH6SJ_!-V!i z8}|X%Z_`Ebw+n++tfZDyn%vF3C;62O#QOXktUT4`Oxy?MS~jkIW1v)OKet#8J%(w4 z*CSkaIG@Io{cln)b)=_}+YjzkL#C|%B;sC1&b)rFVv#Z<+r{%DY7gh;LOEJ}G>Ej} zBzW|-`k&0aRJ2Abjnjbd(8+rZP-o!m7mzYElzB7$!kM$cW&u@hl}{fh<8NM^--2C^ z11y8Rg3oId7GWLSBqZW_uP_*bRZ2rBzacX__Wy8@#&&{&DzV7=-4^T!>_9=QD2kbE zx1#{~NX}dZJ@EuLM>F;BPAsC<{p`9Em@Ju2&dL>O)z4UAA<;8BA3=ze8x`kLW*%6R ad^{O|uM6V0Xe;hNLOol#T=)NE%>My~Eo)H# diff --git a/cfg.js b/cfg.js index b2212d1..51322f7 100644 --- a/cfg.js +++ b/cfg.js @@ -4,8 +4,10 @@ export const SERVER_GAME_MODE = 0; export const SERVER_TICK_INTERVAL = 1; // better dont change export const SERVER_SAVE_INTERVAL = 120000; // all 120s export const SERVER_MAX_CONNECTIONS = 64; -export const SERVER_PLAYER_CONNECTION_TIMEOUT = 30000; // 1min +export const SERVER_PLAYER_CONNECTION_TIMEOUT = 60000; // 1min export const SERVER_DEFAULT_CONSOLE_COLOR = 32; -export const ASSET_DIGEST_PATH = "asset_digest"; \ No newline at end of file +export const ASSET_DIGEST_PATH = "asset_digest"; + +export const MINIMUM_CLIENT_VERSION = "0.31.0"; \ No newline at end of file diff --git a/src/cycle.js b/src/cycle.js index d62487b..b620613 100644 --- a/src/cycle.js +++ b/src/cycle.js @@ -65,8 +65,9 @@ export function playerTimeoutTick() { for (; ii < length; ++ii) { client = this.clients[ii]; if (this.time - client.timeout >= maxTimeout) { - this.print(`${client.remoteAddress} timed out!`, 36); - this.killPlayer(client); + this.print(`${client.remoteAddress} timed out`, 34); + this.savePlayer(client); + this.removePlayer(client); } }; diff --git a/src/index.js b/src/index.js index 5535b1a..cd32369 100644 --- a/src/index.js +++ b/src/index.js @@ -14,6 +14,8 @@ import * as _request from "./request"; import * as _process from "./process"; import * as _database from "./database"; +const greetMessage = fs.readFileSync(".greet", "utf8"); + /** * @class GameServer */ @@ -28,10 +30,11 @@ class GameServer { CRASH: false }; - this.paused = false; - this.proto = null; this.socket = null; + this.player = null; + this.request = null; + this.response = null; this.cycleInstance = null; // Timer things @@ -44,6 +47,7 @@ class GameServer { this.clients = []; + this.greet(); this.setup(); } @@ -64,10 +68,13 @@ class GameServer { } + /** + * @return {HTTP} + */ createHTTPServer() { let server = http.createServer((req, res) => { + this.response = res; if (!this.clientAlreadyConnected(req)) { - this.print(`${req.connection.remoteAddress} connected!`, 36); this.addPlayer(req.connection); } let chunks = []; @@ -75,18 +82,13 @@ class GameServer { chunks.push(chunk); }); req.on("end", () => { - // Reset player timeout - let player = this.getPlayerByRequest(req); - if (player !== null) player.timeout = this.time; - // Data let buffer = Buffer.concat(chunks); req.body = buffer; + this.request = req; this.onRequest(req, res); }); }); - server.listen(CFG.SERVER_PORT, CFG.SERVER_HOST_IP, () => { - - }); + server.listen(CFG.SERVER_PORT); return (server); } @@ -99,6 +101,10 @@ class GameServer { console.log(`\x1b[${color};1m${msg}\x1b[0m`); } + greet() { + console.log(greetMessage); + } + } inherit(GameServer, _setup); diff --git a/src/packets/Envelopes.AuthTicket.js b/src/packets/Envelopes.AuthTicket.js new file mode 100644 index 0000000..91ac460 --- /dev/null +++ b/src/packets/Envelopes.AuthTicket.js @@ -0,0 +1,14 @@ +import proto from "../proto"; + +/** + * @return {Object} + */ +export default function AuthTicket() { + + return ( + new proto.Networking.Envelopes.AuthTicket({ + expire_timestamp_ms: 9999999999999, + }) + ); + +} diff --git a/src/packets/Envelopes.ResponseEnvelope.Auth.js b/src/packets/Envelopes.ResponseEnvelope.Auth.js new file mode 100644 index 0000000..61d1a82 --- /dev/null +++ b/src/packets/Envelopes.ResponseEnvelope.Auth.js @@ -0,0 +1,22 @@ +import proto from "../proto"; + +/** + * @param {Object} obj + * @return {Object} + */ +export default function ResponseEnvelope(obj) { + + return ( + new proto.Networking.Envelopes.ResponseEnvelope({ + status_code: 53, + request_id: obj.id, + api_url: "pgorelease.nianticlabs.com/custom", + auth_ticket: new proto.Networking.Envelopes.AuthTicket({ + start: new Buffer(""), + expire_timestamp_ms: 9999999999999, + end: new Buffer("") + }) + }).encode().toBuffer() + ); + +} \ No newline at end of file diff --git a/src/packets/Envelopes.ResponseEnvelope.js b/src/packets/Envelopes.ResponseEnvelope.js index 7a92dcb..5e05da7 100644 --- a/src/packets/Envelopes.ResponseEnvelope.js +++ b/src/packets/Envelopes.ResponseEnvelope.js @@ -16,8 +16,8 @@ export default function ResponseEnvelope(obj) { }) }), request_id: obj.id, - returns: obj.res - }) + returns: obj.response + }).encode().toBuffer() ); } \ No newline at end of file diff --git a/src/packets/Responses.CheckAwardedBadges.js b/src/packets/Responses.CheckAwardedBadges.js new file mode 100644 index 0000000..61c0bf8 --- /dev/null +++ b/src/packets/Responses.CheckAwardedBadges.js @@ -0,0 +1,17 @@ +import * as CFG from "../../cfg"; + +import proto from "../proto"; + +/** + * @param {Object} obj + * @return {Object} + */ +export default function CheckAwardedBadges(obj) { + + return ( + new proto.Networking.Responses.CheckAwardedBadgesResponse({ + success: true + }).encode() + ); + +} \ No newline at end of file diff --git a/src/packets/Responses.DownloadRemoteConfigVersion.js b/src/packets/Responses.DownloadRemoteConfigVersion.js index 30801d9..8551461 100644 --- a/src/packets/Responses.DownloadRemoteConfigVersion.js +++ b/src/packets/Responses.DownloadRemoteConfigVersion.js @@ -7,8 +7,10 @@ import proto from "../proto"; export default function DownloadRemoteConfigVersion(obj) { return ( - new proto.Networking.Responses.DownloadSettingsResponse({ - hash: "54b359c97e46900f87211ef6e6dd0b7f2a3ea1f5" + new proto.Networking.Responses.DownloadRemoteConfigVersionResponse({ + result: 1, + item_templates_timestamp_ms: 1468540960537, + asset_digest_timestamp_ms: 1467338276561000 }).encode() ); diff --git a/src/packets/Responses.DownloadSettings.js b/src/packets/Responses.DownloadSettings.js index 6fe2d62..f8ea62f 100644 --- a/src/packets/Responses.DownloadSettings.js +++ b/src/packets/Responses.DownloadSettings.js @@ -1,5 +1,7 @@ import proto from "../proto"; +import * as CFG from "../../cfg"; + /** * @param {Object} obj * @return {Object} @@ -8,7 +10,33 @@ export default function DownloadSettings(obj) { return ( new proto.Networking.Responses.DownloadSettingsResponse({ - hash: "b1f2bf509a025b7cd76e1c484e2a24411c50f0612" + hash: "54b359c97e46900f87211ef6e6dd0b7f2a3ea1f5", + settings: new proto.Settings.GlobalSettings({ + fort_settings: new proto.Settings.FortSettings({ + interaction_range_meters: 40, + max_total_deployed_pokemon: 10, + max_player_deployed_pokemon: 1, + deploy_stamina_multiplier: 500, + far_interaction_range_meters: 1000 + }), + map_settings: new proto.Settings.MapSettings({ + pokemon_visible_range: 70, + poke_nav_range_meters: 201, + encounter_range_meters: 50, + get_map_objects_min_refresh_seconds: 5, + get_map_objects_max_refresh_seconds: 30, + get_map_objects_min_distance_meters: 10, + google_maps_api_key: "AIzaSyDF9rkP8lhcddBtvH9gVFzjnNo13WtmJIM" + }), + inventory_settings: new proto.Settings.InventorySettings({ + max_pokemon: 1000, + max_bag_items: 1000, + base_pokemon: 250, + base_bag_items: 350, + base_eggs: 1000 + }), + minimum_client_version: CFG.MINIMUM_CLIENT_VERSION + }) }).encode() ); diff --git a/src/packets/Responses.GetAssetDigest.js b/src/packets/Responses.GetAssetDigest.js index 7a685de..2e74ddf 100644 --- a/src/packets/Responses.GetAssetDigest.js +++ b/src/packets/Responses.GetAssetDigest.js @@ -1,6 +1,9 @@ +import fs from "fs"; +import proto from "../proto"; + import * as CFG from "../../cfg"; -import proto from "../proto"; +let asset = fs.readFileSync(CFG.ASSET_DIGEST_PATH); /** * @param {Object} obj @@ -8,8 +11,6 @@ import proto from "../proto"; */ export default function GetAssetDigest(obj) { - return ( - fs.readFileSync("../../" + CFG.ASSET_DIGEST_PATH) - ); + return (asset); } \ No newline at end of file diff --git a/src/packets/Responses.GetPlayer.js b/src/packets/Responses.GetPlayer.js index f24aa17..1689022 100644 --- a/src/packets/Responses.GetPlayer.js +++ b/src/packets/Responses.GetPlayer.js @@ -6,22 +6,25 @@ import proto from "../proto"; */ function getPlayerDataPacket(obj) { - return new proto.Data.PlayerData({ - creation_timestamp_ms: 1467936859925, - username: obj.username, - team: obj.team, - tutorial_state: obj.tutorial_state, - avatar: new proto.Data.Player.PlayerAvatar(obj.avatar), - max_pokemon_storage: 250, - max_item_storage: 350, - daily_bonus: new proto.Data.Player.DailyBonus({ - next_defender_bonus_collect_timestamp_ms: 1470174535972 - }), - contact_settings: new proto.Data.Player.ContactSettings({ - send_marketing_emails: true - }), - currencies: obj.currencies - }); + return ( + new proto.Data.PlayerData({ + creation_timestamp_ms: 1467936859925, + username: obj.username, + team: obj.team, + tutorial_state: obj.tutorial_state, + avatar: new proto.Data.Player.PlayerAvatar(obj.avatar), + max_pokemon_storage: 250, + max_item_storage: 350, + daily_bonus: new proto.Data.Player.DailyBonus({ + next_defender_bonus_collect_timestamp_ms: 1470174535972 + }), + // equipped_badge: new proto.Data.Player.EquippedBadge({}), + contact_settings: new proto.Data.Player.ContactSettings({ + send_marketing_emails: true + }), + currencies: obj.currencies + }) + ); } diff --git a/src/packets/Responses.GetPlayerProfile.js b/src/packets/Responses.GetPlayerProfile.js new file mode 100644 index 0000000..56d7a7f --- /dev/null +++ b/src/packets/Responses.GetPlayerProfile.js @@ -0,0 +1,25 @@ +import proto from "../proto"; + +/** + * @param {Object} obj + * @return {Object} + */ +export default function GetPlayerProfile(obj) { + + return ( + new proto.Networking.Responses.GetPlayerProfileResponse({ + result: proto.Networking.Responses.GetPlayerProfileResponse.Result.SUCCESS, + start_time: new Date().getTime() * 1000, + badges: [ + new proto.Data.PlayerBadge({ + badge_type: proto.Enums.BadgeType.BADGE_PIKACHU, + rank: 1, + start_value: 0, + end_value: 2000, + current_value: 1337 + }) + ] + }).encode() + ); + +} \ No newline at end of file diff --git a/src/packets/Responses.ItemTemplates.js b/src/packets/Responses.ItemTemplates.js new file mode 100644 index 0000000..3309c15 --- /dev/null +++ b/src/packets/Responses.ItemTemplates.js @@ -0,0 +1,141 @@ +import * as CFG from "../../cfg"; + +import proto from "../proto"; + +/** + * @param {Object} obj + * @return {Object} + */ +export default function ItemTemplates(obj) { + + return ( + new proto.Networking.Responses.DownloadItemTemplatesResponse({ + success: true, + item_templates: new proto.Networking.Responses.DownloadItemTemplatesResponse.ItemTemplate({ + pokemon_settings: null, + // unused but saved here for later use + item_settings: new proto.Settings.Master.ItemSettings({ + "item_id": 702, + "item_type": 6, + "category": 2, + "drop_freq": 0, + "drop_trainer_level": 0, + "pokeball": null, + "potion": null, + "revive": null, + "battle": null, + "food": null, + "inventory_upgrade": null, + "xp_boost": null, + "incense": null, + "egg_incubator": null, + "fort_modifier": null + }), + // unused but saved here for later use + move_settings: new proto.Settings.Master.MoveSettings({ + "movement_id": 13, + "animation_id": 5, + "pokemon_type": 1, + "power": 25, + "accuracy_chance": 1, + "critical_chance": 0.05000000074505806, + "heal_scalar": 0, + "stamina_loss_scalar": 0.05999999865889549, + "trainer_level_min": 1, + "trainer_level_max": 100, + "vfx_name": "wrap", + "duration_ms": 4000, + "damage_window_start_ms": 2800, + "damage_window_end_ms": 3400, + "energy_delta": -20 + }), + move_sequence_settings: new proto.Settings.Master.MoveSequenceSettings({ + "sequence": ["anim attacker atk-move", "f2fvfx attacker acid_fast", "sfx attacker 051-0_acid", "wait 0.15", "vfx defender acid_fast_hit", "anim defender damageS01", "wait 0.35", "sys ui-sync", "sys complete"] + }), + type_effective: new proto.Settings.Master.TypeEffectiveSettings({ + "attack_scalar": [1, 0.800000011920929, 0.800000011920929, 0.800000011920929, 1, 1, 1, 0.800000011920929, 0.800000011920929, 0.800000011920929, 1, 1.25, 1, 1.25, 1, 1, 1.25, 0.800000011920929], + "attack_type": 7 + }), + badge_settings: new proto.Settings.Master.BadgeSettings({ + "badge_type": 13, + "badge_rank": 4, + "targets": [10, 100, 1000] + }), + // unused but saved here for later use + camera: new proto.Settings.Master.CameraSettings({ + "next_camera": "", + "interpolation": [1], + "target_type": [12], + "ease_in_speed": [0], + "east_out_speed": [0], + "duration_seconds": [5], + "wait_seconds": [0], + "transition_seconds": [0.5], + "angle_degree": [-20], + "angle_offset_degree": [0], + "pitch_degree": [20], + "pitch_offset_degree": [0], + "roll_degree": [0], + "distance_meters": [4], + "height_percent": [0], + "vert_ctr_ratio": [1] + }), + player_level: new proto.Settings.Master.PlayerLevelSettings({ + "rank_num": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "required_experience": [0, 1000, 3000, 6000, 10000, 15000, 21000, 28000, 36000, 45000, 55000, 65000, 75000, 85000, 100000, 120000, 140000, 160000, 185000, 210000, 260000, 335000, 435000, 560000, 710000, 900000, 1100000, 1350000, 1650000, 2000000, 2500000, 3000000, 3750000, 4750000, 6000000, 7500000, 9500000, 12000000, 15000000, 20000000], + "cp_multiplier": [0.09399999678134918, 0.16639786958694458, 0.21573247015476227, 0.2557200491428375, 0.29024988412857056, 0.3210875988006592, 0.3492126762866974, 0.37523558735847473, 0.39956727623939514, 0.42250001430511475, 0.443107545375824, 0.4627983868122101, 0.48168495297431946, 0.49985843896865845, 0.517393946647644, 0.5343543291091919, 0.5507926940917969, 0.5667545199394226, 0.5822789072990417, 0.5974000096321106, 0.6121572852134705, 0.6265671253204346, 0.6406529545783997, 0.6544356346130371, 0.667934000492096, 0.6811649203300476, 0.6941436529159546, 0.7068842053413391, 0.719399094581604, 0.7317000031471252, 0.7377694845199585, 0.7437894344329834, 0.7497610449790955, 0.7556855082511902, 0.7615638375282288, 0.7673971652984619, 0.7731865048408508, 0.7789327502250671, 0.7846369743347168, 0.7903000116348267], + "max_egg_player_level": 20, + "max_encounter_player_level": 30 + }), + gym_level: new proto.Settings.Master.GymLevelSettings({ + "required_experience": [0, 2000, 4000, 8000, 12000, 16000, 20000, 30000, 40000, 50000], + "leader_slots": [1, 1, 1, 2, 2, 2, 3, 3, 3, 4], + "trainer_slots": [0, 1, 2, 2, 3, 4, 4, 5, 6, 6], + "search_roll_bonus": [] + }), + battle_settings: new proto.Settings.Master.GymBattleSettings({ + "energy_per_sec": 0, + "dodge_energy_cost": 0, + "retarget_seconds": 0.5, + "enemy_attack_interval": 1.5, + "attack_server_interval": 5, + "round_duration_seconds": 99, + "bonus_time_per_ally_seconds": 10, + "maximum_attackers_per_battle": 20, + "same_type_attack_bonus_multiplier": 1.25, + "maximum_energy": 100, + "energy_delta_per_health_lost": 0.5, + "dodge_duration_ms": 500, + "minimum_player_level": 5, + "swap_duration_ms": 1000 + }), + encounter_settings: new proto.Settings.Master.EncounterSettings({ + "spin_bonus_threshold": 0.5, + "excellent_throw_threshold": 1.7000000476837158, + "great_throw_threshold": 1.2999999523162842, + "nice_throw_threshold": 1, + "milestone_threshold": 100 + }), + iap_item_display: null, // seems like useless?? + iap_settings: new proto.Settings.Master.IapSettings({ + "daily_bonus_coins": 0, + "daily_defender_bonus_per_pokemon": [500, 10], + "daily_defender_bonus_max_defenders": 10, + "daily_defender_bonus_currency": ["STARDUST", "POKECOIN"], + "min_time_between_claims_ms": 0, + "daily_bonus_enabled": false, + "daily_defender_bonus_enabled": true + }), + pokemon_upgrades: new proto.Settings.Master.PokemonUpgradeSettings({ + "upgrades_per_level": 2, + "allowed_levels_above_player": 2, + "candy_cost": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 15, 15], + "stardust_cost": [200, 200, 400, 400, 600, 600, 800, 800, 1000, 1000, 1300, 1300, 1600, 1600, 1900, 1900, 2200, 2200, 2500, 2500, 3000, 3000, 3500, 3500, 4000, 4000, 4500, 4500, 5000, 5000, 6000, 6000, 7000, 7000, 8000, 8000, 9000, 9000, 10000, 10000] + }), + equipped_badges: null // seems like unused + }), + timestamp_ms: 1468540960537 + }).encode() + ); + +} \ No newline at end of file diff --git a/src/packets/index.js b/src/packets/index.js index b615cba..0aa276e 100644 --- a/src/packets/index.js +++ b/src/packets/index.js @@ -5,6 +5,11 @@ export GetAssetDigest from "./Responses.GetAssetDigest"; export GetHatchedEggs from "./Responses.GetHatchedEggs"; export GetInventory from "./Responses.GetInventory"; export GetPlayer from "./Responses.GetPlayer"; +export GetPlayerProfile from "./Responses.GetPlayerProfile"; + +export ItemTemplates from "./Responses.ItemTemplates"; +export CheckAwardedBadges from "./Responses.CheckAwardedBadges"; export AuthTicket from "./Envelopes.AuthTicket"; -export ResponseEnvelope from "./Envelopes.ResponseEnvelope"; \ No newline at end of file +export ResponseEnvelope from "./Envelopes.ResponseEnvelope"; +export ResponseEnvelopeAuth from "./Envelopes.ResponseEnvelope.Auth"; \ No newline at end of file diff --git a/src/player.js b/src/player.js index 07fb5ad..16ce3cf 100644 --- a/src/player.js +++ b/src/player.js @@ -1,18 +1,98 @@ +import proto from "./proto"; + import * as CFG from "../cfg"; -export function killPlayer(player) { +import { + getHashCodeFrom, + decodeRequestEnvelope +} from "./utils"; - let index = this.getPlayerIndex(player); +/** + * @class Player + */ +class Player { + + /** @constructor */ + constructor(obj) { + + this.uid = -1; + + this.name = null; + + this.email = null; + + this.position = { + latitude: 0, + longitude: 0, + altitude: 0 + }; + + this.exp = 0; + + this.stardust = 0; + this.pokecoins = 0; + + this.avatar = null; + this.badges = null; + this.pokedex = null; + this.inventory = null; + + this.response = obj.response; + this.connection = obj.connection; + + this.timeout = obj.timeout; + + this.remotePort = obj.remotePort; + this.remoteAddress = obj.remoteAddress; + + this.currentEncounter = null; + + this.loggedIn = obj.loggedIn || false; + this.authenticated = false; - if (index >= 0) { - this.clients.splice(index, 1); } - else { - this.print("Failed at killing player", 33); + + /** + * @param {String} email + */ + generateUid(email) { + this.uid = getHashCodeFrom(String(email)); + } + + /** + * @param {Request} req + */ + updatePosition(req) { + + let data = decodeRequestEnvelope(req.request_message.buffer); + + this.latitude = data.latitude; + this.longitude = data.longitude; + //this.position.altitude = data.altitude; + + //console.log(`Updated position: ${data.latitude};${data.longitude}`); + + } + + get latitude() { + return (this.position.latitude); + } + set latitude(lat) { + this.position.latitude = lat; + } + + get longitude() { + return (this.position.longitude); + } + set longitude(lng) { + this.position.longitude = lng; } } +/** + * @param {Player} player + */ export function getPlayerIndex(player) { let ip = player.remoteAddress; @@ -31,12 +111,18 @@ export function getPlayerIndex(player) { } +/** + * @param {Request} req + */ export function getPlayerByRequest(req) { return ( this.getPlayerByIP(req.connection.remoteAddress) ); } +/** + * @param {String} ip + */ export function getPlayerByIP(ip) { let ii = 0, length = this.clients.length; @@ -51,15 +137,35 @@ export function getPlayerByIP(ip) { } +/** + * @param {Request} connection + */ export function addPlayer(connection) { - this.clients.push({ - name: "rofl", + this.clients.push(new Player({ timeout: this.time, + connection: connection, + response: this.response, remotePort: connection.remotePort, - remoteAddress: connection.remoteAddress, - connection: connection - }); + remoteAddress: connection.remoteAddress + })); + +} + +/** + * @param {Player} player + */ +export function removePlayer(player) { + + let index = this.getPlayerIndex(player); + + if (index >= 0) { + this.clients.splice(index, 1); + this.print(`${player.remoteAddress} disconnected!`, 36); + } + else { + this.print("Failed at removing player", 33); + } } @@ -69,6 +175,14 @@ export function updatePlayers() { } export function savePlayers() { - this.print("Saving players into database"); + this.print("Saving players into database.."); + return void 0; +} + +/** + * @param {Player} player + */ +export function savePlayer(player) { + this.print(`${player.remoteAddress} saved into database`, 34); return void 0; } \ No newline at end of file diff --git a/src/process.js b/src/process.js index 7c2013d..f50ede9 100644 --- a/src/process.js +++ b/src/process.js @@ -1,10 +1,11 @@ import * as CFG from "../cfg"; -function processCommand(cmd, data) { +export function processCommand(cmd, data) { switch (cmd) { // How many active connections there are case "/clients": - this.print("There are many connected players!", 33); + let length = this.clients.length; + this.print(`${length} connected player${length === 1 ? "": "s"}!`, 33); break; // Kill the server case "/exit": @@ -19,7 +20,7 @@ export function stdinInput(data) { if (data.length < 1) return void 0; data = data.split(" "); var cmd = data[0]; - processCommand(); + this.processCommand(cmd, data); }; export function uncaughtException(excp) { diff --git a/src/utils.js b/src/utils.js index ac4638d..7eaa8d2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,3 +1,5 @@ +import proto from "./proto"; + /** * @param {Object} cls * @param {Object} prot @@ -13,4 +15,29 @@ export function inherit(cls, prot) { } }; -} \ No newline at end of file +} + +/** + * @param {Buffer} body + */ +export function decodeRequestEnvelope(body) { + return ( + proto.Networking.Envelopes.RequestEnvelope.decode(body) + ); +} + +/** + * http://stackoverflow.com/a/7616484/3367904 + * @param {String} str + * @return {String} + */ +export function getHashCodeFrom(str) { + var hash = 0, i, chr, len; + if (str.length === 0) return hash; + for (i = 0, len = str.length; i < len; i++) { + chr = str.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +}; \ No newline at end of file