├── .babelrc
├── .gitattributes
├── .gitignore
├── .greet
├── .help
├── .travis.yml
├── Dockerfile
├── LICENSE
├── README.md
├── api
├── cfg.js.example
├── css
│ ├── main.css
│ ├── pure.min.css
│ └── vex.css
├── img
│ ├── gym_NEUTRAL.png
│ ├── gym_blue.png
│ ├── gym_red.png
│ ├── gym_yellow.png
│ ├── license.txt
│ ├── pokestop_blue.png
│ ├── pokestop_lure.png
│ ├── pokestop_puple.png
│ └── spawn_point.png
├── index.html
├── index.js
└── js
│ ├── ajax.js
│ ├── gmaps.js
│ ├── init.js
│ └── main.js
├── cfg.js.example
├── install-windows.bat
├── package.json
├── src
├── api.js
├── commands.js
├── cycle.js
├── db
│ ├── create.js
│ ├── get.js
│ ├── index.js
│ ├── query.js
│ └── tables
│ │ ├── gym.table
│ │ ├── owned_pkmn.table
│ │ ├── pokestop.table
│ │ ├── spawn_points.table
│ │ └── users.table
├── dump.js
├── enum.js
├── http.js
├── index.js
├── models
│ ├── GameMaster
│ │ └── index.js
│ ├── Player
│ │ ├── Avatar
│ │ │ └── index.js
│ │ ├── Bag
│ │ │ └── index.js
│ │ ├── CandyBag
│ │ │ └── index.js
│ │ ├── Contact
│ │ │ └── index.js
│ │ ├── Currency
│ │ │ └── index.js
│ │ ├── Info
│ │ │ └── index.js
│ │ ├── Party
│ │ │ └── index.js
│ │ ├── PokeDex
│ │ │ └── index.js
│ │ ├── Tutorial
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── packets
│ │ │ ├── CheckAwardedBadges.js
│ │ │ ├── ClaimCodename.js
│ │ │ ├── GetAssetDigest.js
│ │ │ ├── GetAuthTicket.js
│ │ │ ├── GetHatchedEggs.js
│ │ │ ├── GetInventory.js
│ │ │ ├── GetPlayer.js
│ │ │ ├── GetPlayerProfile.js
│ │ │ ├── LevelUpRewards.js
│ │ │ ├── NicknamePokemon.js
│ │ │ ├── RecycleInventoryItem.js
│ │ │ ├── ReleasePokemon.js
│ │ │ ├── SetAvatar.js
│ │ │ ├── SetFavoritePokemon.js
│ │ │ ├── UpgradePokemon.js
│ │ │ └── index.js
│ ├── Pokemon
│ │ ├── WildPokemon
│ │ │ └── index.js
│ │ ├── action.js
│ │ ├── calc.js
│ │ └── index.js
│ └── World
│ │ ├── Cell
│ │ └── index.js
│ │ ├── Fort
│ │ ├── Gym
│ │ │ └── index.js
│ │ ├── Pokestop
│ │ │ └── index.js
│ │ └── index.js
│ │ ├── MapObject
│ │ └── index.js
│ │ ├── SpawnPoint
│ │ └── index.js
│ │ ├── forts.js
│ │ ├── index.js
│ │ ├── packets
│ │ ├── CatchPokemon.js
│ │ ├── CheckChallenge.js
│ │ ├── DownloadItemTemplates.js
│ │ ├── DownloadRemoteConfigVersion.js
│ │ ├── DownloadSettings.js
│ │ ├── Encounter.js
│ │ ├── FortDetails.js
│ │ ├── FortSearch.js
│ │ ├── GetDownloadUrls.js
│ │ ├── GetMapObjects.js
│ │ └── index.js
│ │ └── players.js
├── modes
│ └── index.js
├── print.js
├── process.js
├── request.js
├── response.js
├── setup.js
├── shared.js
└── utils.js
├── supervisord.conf
└── updater.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0"]
3 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Line endings: enforce LF in GitHub, convert to native on checkout.
2 |
3 | * text=auto
4 | *.js text
5 |
6 | # Make GitHub ignore vendor libraries while computing language stats.
7 | # See https://github.com/github/linguist#overrides.
8 |
9 | *.proto linguist-vendored=true
10 | *.sh linguist-vendored=true
11 | *.bat linguist-vendored=true
12 | *.html linguist-vendored=true
13 | *.css linguist-vendored=true
14 |
15 | api/* linguist-vendored=true
16 |
17 | # Explicitly specify language for non-standard extensions used under
18 | # ide/web/lib/templates to make GitHub correctly count their language stats.
19 | #
20 | *.js_ linguist-language=JavaScript
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | scripts/
3 | data/
4 | logs/
5 | cfg.js
6 | .save
7 | npm-debug.*
8 |
--------------------------------------------------------------------------------
/.greet:
--------------------------------------------------------------------------------
1 | ______ _____ _____ _____
2 | | ___ \ _ | __ \ _ |
3 | | |_/ / | | | | \/ | | | ___ ___ _ ____ _____ _ __
4 | | __/| | | | | __| | | |/ __|/ _ \ '__\ \ / / _ \ '__|
5 | | | \ \_/ / |_\ \ \_/ /\__ \ __/ | \ V / __/ |
6 | \_| \___/ \____/\___/ |___/\___|_| \_/ \___|_|
7 |
--------------------------------------------------------------------------------
/.help:
--------------------------------------------------------------------------------
1 | players : How many players are connected
2 | exit : Exit the server
3 | kick [Username] : Kick player by username
4 | kickall : Kick all players
5 | clear : Clear the server console
6 | save : Save all players into database
7 | spawn [Username] [Pkmn] [Amount] : Spawn pokemons at users position
8 | dump : Dump assets
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | script: "npm run test"
4 | node_js:
5 | - "4"
6 | - "5"
7 | - "6"
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:latest
2 | MAINTAINER Draco Miles X
3 | RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -qq mysql-server mysql-client -y && apt-get upgrade -y && apt-get dist-upgrade -y
4 | RUN apt-get install -y \
5 | apt-utils \
6 | nano \
7 | build-essential \
8 | curl \
9 | lsb-release \
10 | openssl \
11 | libssl-dev \
12 | openssh-server \
13 | openssh-client \
14 | sudo \
15 | python \
16 | python-dev \
17 | python-pip \
18 | python3 \
19 | python3-dev \
20 | python3-pip \
21 | pkg-config \
22 | autoconf \
23 | automake \
24 | libtool \
25 | curl \
26 | make \
27 | g++ \
28 | unzip \
29 | supervisor
30 | RUN mkdir -p /var/run/sshd /var/log/supervisor
31 | RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
32 | RUN apt-get install -y \
33 | nodejs \
34 | git
35 | RUN git clone --recursive https://github.com/google/protobuf.git
36 | RUN cd /protobuf && ./autogen.sh && ./configure && make && make check && make install && ldconfig
37 | RUN cd /
38 | RUN git clone --recursive https://github.com/maierfelix/POGOserver.git
39 | COPY cfg.js /POGOserver/
40 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
41 | RUN service mysql start && mysql -u root -e "create database pogosql";
42 | EXPOSE 22 80 443 3306 3000
43 | CMD ["/usr/bin/supervisord"]
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ````
2 | ______ _____ _____ _____
3 | | ___ \ _ | __ \ _ |
4 | | |_/ / | | | | \/ | | | ___ ___ _ ____ _____ _ __
5 | | __/| | | | | __| | | |/ __|/ _ \ '__\ \ / / _ \ '__|
6 | | | \ \_/ / |_\ \ \_/ /\__ \ __/ | \ V / __/ |
7 | \_| \___/ \____/\___/ |___/\___|_| \_/ \___|_|
8 | ````
9 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | # Getting started
27 |
28 | ## Setup
29 |
30 | Copy and rename ``cfg.js.example`` to ``cfg.js``.
31 |
32 | Open ``cfg.js`` and fill the following fields:
33 |
34 | ````js
35 | DOWNLOAD_PROVIDER: "GOOGLE";
36 | DOWNLOAD_USERNAME: "USERNAME";
37 | DOWNLOAD_PASSWORD: "PASSWORD";
38 | ````
39 |
40 | ## Tunneling setup
41 | The pokemon go app traffic has to get forwarded manually to this custom server. Download [rastapasta](https://github.com/rastapasta)'s [Pokemon Go Xposed](https://github.com/rastapasta/pokemon-go-xposed/releases) app and follow the installation instructions [here](https://github.com/rastapasta/pokemon-go-xposed#how-to-use-it).
42 |
43 | ## Database setup
44 |
45 | To setup a database connection, open ``cfg.js`` and change the database login credentials:
46 |
47 | ````js
48 | MYSQL_PORT: 3306,
49 | MYSQL_HOST_IP: "127.0.0.1",
50 | MYSQL_DB_NAME: "pogosql",
51 | MYSQL_USERNAME: "root",
52 | MYSQL_PASSWORD: "",
53 | ````
54 |
55 | The required database tables get generated automatically.
56 |
57 | ## Server setup
58 |
59 | You need at minimum [Node.js](https://nodejs.org/en/) version 6.x.
60 |
61 | Open up a terminal and enter ``npm run boot`` to start the server or ``npm run api`` to start the web-api.
62 |
63 | To Update the Server enter ``npm run update``
64 |
65 | ## Docker setup
66 |
67 | 1. Download ``Dockerfile``, ``cfg.js.example`` and ``supervisord.conf`` from github.
68 | 2. Place ``Dockerfile``, ``cfg.js.example`` and ``supervisord.conf`` into the same folder. Rename ``cfg.js.example`` to ``cfg.js``.
69 | 3. Modify ``cfg.js`` to your requirements as described above.
70 | 4. Create a container and run it.
71 | 5. Open a bash prompt, enter: ``cd /POGOserver/`` and enter ``npm run boot``.
72 | 6. Connect the Pokemon Go app to the server.
73 | 7. Done.
74 |
75 | Note: Instead of automatically mapping the ports, map them static, so they don't change after reboot.
76 |
--------------------------------------------------------------------------------
/api/cfg.js.example:
--------------------------------------------------------------------------------
1 | const CFG = {
2 | API: {
3 | HOST: "127.0.0.1",
4 | PORT: 3000,
5 | ROUTE: "/api"
6 | },
7 | GMAPS: {
8 | API_KEY: "XXXXX",
9 | BASE_ZOOM: 20,
10 | BASE_LAT: 39.18875480450959,
11 | BASE_LNG: -96.58109955489635
12 | }
13 | };
--------------------------------------------------------------------------------
/api/css/main.css:
--------------------------------------------------------------------------------
1 | html {
2 | -webkit-animation: fadein 1s;
3 | -moz-animation: fadein 1s;
4 | -ms-animation: fadein 1s;
5 | -o-animation: fadein 1s;
6 | animation: fadein 1s;
7 | }
8 |
9 | body {
10 | overflow-x: hidden;
11 | overflow-y: scroll !important;
12 | }
13 |
14 | #map {
15 | position: absolute;
16 | left: calc(50% - 400px);
17 | width: 800px !important;
18 | height: 400px !important;
19 | }
20 |
21 | #map_manager {
22 | padding-bottom: 500px;
23 | margin-top: -100px;
24 | display: none;
25 | }
26 |
27 | .view {
28 | letter-spacing: 0px;
29 | padding: 5px 25px !important;
30 | margin-top: -35px;
31 | }
32 |
33 | .area {
34 | font-family: "Andale Mono", AndaleMono, monospace;
35 | text-align: center;
36 | font-style: normal;
37 | font-variant: normal;
38 | -webkit-font-smoothing: antialiased;
39 | font-size: 75px;
40 | letter-spacing: -5px;
41 | color: #fff;
42 | position: relative;
43 | margin-top: 5px;
44 | text-transform: uppercase;
45 | }
46 |
47 | .Codemirror {
48 | text-align: left;
49 | }
50 |
51 | .ping {
52 | position: absolute;
53 | top: 0px;
54 | left: 10px;
55 | font-size: 18px;
56 | letter-spacing: 0px;
57 | }
58 |
59 | .version {
60 | font-size: 15px;
61 | letter-spacing: 0px;
62 | margin-top: -75px;
63 | margin-bottom: -25px;
64 | text-transform: none;
65 | }
66 |
67 | .btn {
68 | text-transform: uppercase;
69 | }
70 |
71 | .centered {
72 | text-align: center;
73 | position: relative;
74 | top: 40%;
75 | transform: translateY(-40%);
76 | margin-top: -175px;
77 | -webkit-transform: translateY(-40%);
78 | -moz-transform: translateY(0%);
79 | }
80 |
81 | @-moz-document url-prefix() {
82 | .centered {
83 | margin-top: 0px;
84 | }
85 | }
86 |
87 | .body {
88 | background: #2d2d2d;
89 | background-color: #2d2d2d;
90 | }
91 |
92 | .star {
93 | position: absolute;
94 | width: 2px;
95 | height: 2px;
96 | background: rgba(255, 255, 255, 0.45);
97 | opacity: 1;
98 | }
99 |
100 | .btn {
101 | font-family: "Andale Mono", AndaleMono, monospace;
102 | display: inline-block;
103 | padding: 5px;
104 | border: 1px solid rgba(255, 255, 255, .35);
105 | border-radius: 4px;
106 | color: rgba(255, 255, 255, .75);
107 | text-decoration: none;
108 | transition: border .35s, background .35s;
109 | min-width: 100px;
110 | text-align: center;
111 | font-size: 15px;
112 | font-style: normal;
113 | font-variant: normal;
114 | font-weight: 500;
115 | line-height: 25px;
116 | cursor: pointer;
117 | }
118 |
119 | .input {
120 | background: rgba(0,0,0,0);
121 | cursor: auto;
122 | }
123 |
124 | input:focus {
125 | outline: none;
126 | }
127 |
128 | button:focus {
129 | outline: none;
130 | }
131 |
132 | .label {
133 | background: rgba(255, 255, 255, .05);
134 | border: 1px solid rgba(255, 255, 255, .5);
135 | color: #79a2b7;
136 | }
137 |
138 | .info {
139 | padding-bottom: 45px;
140 | margin-top: -45px;
141 | font-size: 18px;
142 | letter-spacing: 0px;
143 | }
144 |
145 | .cmd_label {
146 | border: none;
147 | background: rgba(255,255,255,0.05);
148 | color: white;
149 | cursor: auto;
150 | text-transform: none;
151 | }
152 |
153 | .submit {
154 | background: rgba(165, 165, 165, 0.15);
155 | }
156 |
157 | .with_label {
158 | margin-left: 25px;
159 | width: 120px;
160 | }
161 |
162 | #connection_status {
163 | text-transform: uppercase;
164 | }
165 |
166 | .login_area {
167 | font-size: 25px;
168 | letter-spacing: 0px;
169 | text-transform: none;
170 | margin: -25px;
171 | padding: 0px;
172 | }
173 |
174 | .login_input {
175 | text-transform: none;
176 | background: rgba(0,0,0,0);
177 | }
178 |
179 | .noselect {
180 | -webkit-touch-callout: none;
181 | -webkit-user-select: none;
182 | -khtml-user-select: none;
183 | -moz-user-select: none;
184 | -ms-user-select: none;
185 | user-select: none;
186 | }
187 |
188 | .btn:hover {
189 | background: rgba(255, 255, 255, .05);
190 | border: 1px solid rgba(255, 255, 255, .5);
191 | }
192 |
193 | .txt {
194 | margin-left: calc(20%);
195 | margin-right: calc(20%);
196 | left: auto;
197 | width: auto;
198 | min-height: 300px;
199 | background: rgba(255, 255, 255, 0.0) !important;
200 | border: 1px solid rgba(255, 255, 255, 0.0) !important;
201 | border-radius: 4px !important;
202 | padding: 10px 15px !important;
203 | color: rgba(255, 255, 255, .75) !important;
204 | resize: none;
205 | transition: color .35s !important;
206 | }
207 |
208 | .txt:focus {
209 | outline: none;
210 | color: rgba(255, 255, 255, .85);
211 | }
212 |
213 | ::-moz-selection {
214 | color: rgba(255, 255, 255, .85);
215 | background: rgba(255, 255, 255, .075);
216 | }
217 | ::selection {
218 | color: rgba(255, 255, 255, .85);
219 | background: rgba(255, 255, 255, .075);
220 | }
221 |
222 | ::-webkit-scrollbar{
223 | width: 10px;
224 | height: 0px;
225 | background: transparent;
226 | }
227 | ::-webkit-scrollbar-thumb{
228 | background: rgba(255, 255, 255, .15);
229 | border-radius: 5px;
230 | }
231 | ::-webkit-scrollbar-corner{
232 | background: transparent;
233 | }
234 |
235 | @keyframes fadein {
236 | from { opacity: 0; }
237 | to { opacity: 1; }
238 | }
239 | @-webkit-keyframes fadein {
240 | from { opacity: 0; }
241 | to { opacity: 1; }
242 | }
243 | @-moz-keyframes fadein {
244 | from { opacity: 0; }
245 | to { opacity: 1; }
246 | }
247 |
248 | @keyframes pulsate {
249 | 0% {transform: scale(0.1, 0.1); opacity: 0.0;}
250 | 50% {opacity: 1.0;}
251 | 100% {transform: scale(1.2, 1.2); opacity: 0.0;}
252 | }
253 | @-webkit-keyframes pulsate {
254 | 0% {-webkit-transform: scale(0.1, 0.1); opacity: 0.0;}
255 | 50% {opacity: 1.0;}
256 | 100% {-webkit-transform: scale(1.2, 1.2); opacity: 0.0;}
257 | }
258 | @-moz-keyframes pulsate {
259 | 0% {transform: scale(0.1, 0.1); opacity: 0.0;}
260 | 50% {opacity: 1.0;}
261 | 100% {transform: scale(1.2, 1.2); opacity: 0.0;}
262 | }
--------------------------------------------------------------------------------
/api/css/pure.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | Pure v0.6.0
3 | Copyright 2014 Yahoo! Inc. All rights reserved.
4 | Licensed under the BSD License.
5 | https://github.com/yahoo/pure/blob/master/LICENSE.md
6 | */
7 | /*!
8 | normalize.css v^3.0 | MIT License | git.io/normalize
9 | Copyright (c) Nicolas Gallagher and Jonathan Neal
10 | */
11 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}/*!
12 | Pure v0.6.0
13 | Copyright 2014 Yahoo! Inc. All rights reserved.
14 | Licensed under the BSD License.
15 | https://github.com/yahoo/pure/blob/master/LICENSE.md
16 | */
17 | .pure-g{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap;-ms-align-content:flex-start;-webkit-align-content:flex-start;align-content:flex-start}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class *="pure-u"]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%;*width:4.1357%}.pure-u-1-12,.pure-u-2-24{width:8.3333%;*width:8.3023%}.pure-u-1-8,.pure-u-3-24{width:12.5%;*width:12.469%}.pure-u-1-6,.pure-u-4-24{width:16.6667%;*width:16.6357%}.pure-u-1-5{width:20%;*width:19.969%}.pure-u-5-24{width:20.8333%;*width:20.8023%}.pure-u-1-4,.pure-u-6-24{width:25%;*width:24.969%}.pure-u-7-24{width:29.1667%;*width:29.1357%}.pure-u-1-3,.pure-u-8-24{width:33.3333%;*width:33.3023%}.pure-u-3-8,.pure-u-9-24{width:37.5%;*width:37.469%}.pure-u-2-5{width:40%;*width:39.969%}.pure-u-5-12,.pure-u-10-24{width:41.6667%;*width:41.6357%}.pure-u-11-24{width:45.8333%;*width:45.8023%}.pure-u-1-2,.pure-u-12-24{width:50%;*width:49.969%}.pure-u-13-24{width:54.1667%;*width:54.1357%}.pure-u-7-12,.pure-u-14-24{width:58.3333%;*width:58.3023%}.pure-u-3-5{width:60%;*width:59.969%}.pure-u-5-8,.pure-u-15-24{width:62.5%;*width:62.469%}.pure-u-2-3,.pure-u-16-24{width:66.6667%;*width:66.6357%}.pure-u-17-24{width:70.8333%;*width:70.8023%}.pure-u-3-4,.pure-u-18-24{width:75%;*width:74.969%}.pure-u-19-24{width:79.1667%;*width:79.1357%}.pure-u-4-5{width:80%;*width:79.969%}.pure-u-5-6,.pure-u-20-24{width:83.3333%;*width:83.3023%}.pure-u-7-8,.pure-u-21-24{width:87.5%;*width:87.469%}.pure-u-11-12,.pure-u-22-24{width:91.6667%;*width:91.6357%}.pure-u-23-24{width:95.8333%;*width:95.8023%}.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{width:100%}/*!
18 | Pure v0.6.0
19 | Copyright 2014 Yahoo! Inc. All rights reserved.
20 | Licensed under the BSD License.
21 | https://github.com/yahoo/pure/blob/master/LICENSE.md
22 | */
23 | @media screen and (min-width:35.5em){.pure-u-sm-1,.pure-u-sm-1-1,.pure-u-sm-1-2,.pure-u-sm-1-3,.pure-u-sm-2-3,.pure-u-sm-1-4,.pure-u-sm-3-4,.pure-u-sm-1-5,.pure-u-sm-2-5,.pure-u-sm-3-5,.pure-u-sm-4-5,.pure-u-sm-5-5,.pure-u-sm-1-6,.pure-u-sm-5-6,.pure-u-sm-1-8,.pure-u-sm-3-8,.pure-u-sm-5-8,.pure-u-sm-7-8,.pure-u-sm-1-12,.pure-u-sm-5-12,.pure-u-sm-7-12,.pure-u-sm-11-12,.pure-u-sm-1-24,.pure-u-sm-2-24,.pure-u-sm-3-24,.pure-u-sm-4-24,.pure-u-sm-5-24,.pure-u-sm-6-24,.pure-u-sm-7-24,.pure-u-sm-8-24,.pure-u-sm-9-24,.pure-u-sm-10-24,.pure-u-sm-11-24,.pure-u-sm-12-24,.pure-u-sm-13-24,.pure-u-sm-14-24,.pure-u-sm-15-24,.pure-u-sm-16-24,.pure-u-sm-17-24,.pure-u-sm-18-24,.pure-u-sm-19-24,.pure-u-sm-20-24,.pure-u-sm-21-24,.pure-u-sm-22-24,.pure-u-sm-23-24,.pure-u-sm-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-sm-1-24{width:4.1667%;*width:4.1357%}.pure-u-sm-1-12,.pure-u-sm-2-24{width:8.3333%;*width:8.3023%}.pure-u-sm-1-8,.pure-u-sm-3-24{width:12.5%;*width:12.469%}.pure-u-sm-1-6,.pure-u-sm-4-24{width:16.6667%;*width:16.6357%}.pure-u-sm-1-5{width:20%;*width:19.969%}.pure-u-sm-5-24{width:20.8333%;*width:20.8023%}.pure-u-sm-1-4,.pure-u-sm-6-24{width:25%;*width:24.969%}.pure-u-sm-7-24{width:29.1667%;*width:29.1357%}.pure-u-sm-1-3,.pure-u-sm-8-24{width:33.3333%;*width:33.3023%}.pure-u-sm-3-8,.pure-u-sm-9-24{width:37.5%;*width:37.469%}.pure-u-sm-2-5{width:40%;*width:39.969%}.pure-u-sm-5-12,.pure-u-sm-10-24{width:41.6667%;*width:41.6357%}.pure-u-sm-11-24{width:45.8333%;*width:45.8023%}.pure-u-sm-1-2,.pure-u-sm-12-24{width:50%;*width:49.969%}.pure-u-sm-13-24{width:54.1667%;*width:54.1357%}.pure-u-sm-7-12,.pure-u-sm-14-24{width:58.3333%;*width:58.3023%}.pure-u-sm-3-5{width:60%;*width:59.969%}.pure-u-sm-5-8,.pure-u-sm-15-24{width:62.5%;*width:62.469%}.pure-u-sm-2-3,.pure-u-sm-16-24{width:66.6667%;*width:66.6357%}.pure-u-sm-17-24{width:70.8333%;*width:70.8023%}.pure-u-sm-3-4,.pure-u-sm-18-24{width:75%;*width:74.969%}.pure-u-sm-19-24{width:79.1667%;*width:79.1357%}.pure-u-sm-4-5{width:80%;*width:79.969%}.pure-u-sm-5-6,.pure-u-sm-20-24{width:83.3333%;*width:83.3023%}.pure-u-sm-7-8,.pure-u-sm-21-24{width:87.5%;*width:87.469%}.pure-u-sm-11-12,.pure-u-sm-22-24{width:91.6667%;*width:91.6357%}.pure-u-sm-23-24{width:95.8333%;*width:95.8023%}.pure-u-sm-1,.pure-u-sm-1-1,.pure-u-sm-5-5,.pure-u-sm-24-24{width:100%}}@media screen and (min-width:48em){.pure-u-md-1,.pure-u-md-1-1,.pure-u-md-1-2,.pure-u-md-1-3,.pure-u-md-2-3,.pure-u-md-1-4,.pure-u-md-3-4,.pure-u-md-1-5,.pure-u-md-2-5,.pure-u-md-3-5,.pure-u-md-4-5,.pure-u-md-5-5,.pure-u-md-1-6,.pure-u-md-5-6,.pure-u-md-1-8,.pure-u-md-3-8,.pure-u-md-5-8,.pure-u-md-7-8,.pure-u-md-1-12,.pure-u-md-5-12,.pure-u-md-7-12,.pure-u-md-11-12,.pure-u-md-1-24,.pure-u-md-2-24,.pure-u-md-3-24,.pure-u-md-4-24,.pure-u-md-5-24,.pure-u-md-6-24,.pure-u-md-7-24,.pure-u-md-8-24,.pure-u-md-9-24,.pure-u-md-10-24,.pure-u-md-11-24,.pure-u-md-12-24,.pure-u-md-13-24,.pure-u-md-14-24,.pure-u-md-15-24,.pure-u-md-16-24,.pure-u-md-17-24,.pure-u-md-18-24,.pure-u-md-19-24,.pure-u-md-20-24,.pure-u-md-21-24,.pure-u-md-22-24,.pure-u-md-23-24,.pure-u-md-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-md-1-24{width:4.1667%;*width:4.1357%}.pure-u-md-1-12,.pure-u-md-2-24{width:8.3333%;*width:8.3023%}.pure-u-md-1-8,.pure-u-md-3-24{width:12.5%;*width:12.469%}.pure-u-md-1-6,.pure-u-md-4-24{width:16.6667%;*width:16.6357%}.pure-u-md-1-5{width:20%;*width:19.969%}.pure-u-md-5-24{width:20.8333%;*width:20.8023%}.pure-u-md-1-4,.pure-u-md-6-24{width:25%;*width:24.969%}.pure-u-md-7-24{width:29.1667%;*width:29.1357%}.pure-u-md-1-3,.pure-u-md-8-24{width:33.3333%;*width:33.3023%}.pure-u-md-3-8,.pure-u-md-9-24{width:37.5%;*width:37.469%}.pure-u-md-2-5{width:40%;*width:39.969%}.pure-u-md-5-12,.pure-u-md-10-24{width:41.6667%;*width:41.6357%}.pure-u-md-11-24{width:45.8333%;*width:45.8023%}.pure-u-md-1-2,.pure-u-md-12-24{width:50%;*width:49.969%}.pure-u-md-13-24{width:54.1667%;*width:54.1357%}.pure-u-md-7-12,.pure-u-md-14-24{width:58.3333%;*width:58.3023%}.pure-u-md-3-5{width:60%;*width:59.969%}.pure-u-md-5-8,.pure-u-md-15-24{width:62.5%;*width:62.469%}.pure-u-md-2-3,.pure-u-md-16-24{width:66.6667%;*width:66.6357%}.pure-u-md-17-24{width:70.8333%;*width:70.8023%}.pure-u-md-3-4,.pure-u-md-18-24{width:75%;*width:74.969%}.pure-u-md-19-24{width:79.1667%;*width:79.1357%}.pure-u-md-4-5{width:80%;*width:79.969%}.pure-u-md-5-6,.pure-u-md-20-24{width:83.3333%;*width:83.3023%}.pure-u-md-7-8,.pure-u-md-21-24{width:87.5%;*width:87.469%}.pure-u-md-11-12,.pure-u-md-22-24{width:91.6667%;*width:91.6357%}.pure-u-md-23-24{width:95.8333%;*width:95.8023%}.pure-u-md-1,.pure-u-md-1-1,.pure-u-md-5-5,.pure-u-md-24-24{width:100%}}@media screen and (min-width:64em){.pure-u-lg-1,.pure-u-lg-1-1,.pure-u-lg-1-2,.pure-u-lg-1-3,.pure-u-lg-2-3,.pure-u-lg-1-4,.pure-u-lg-3-4,.pure-u-lg-1-5,.pure-u-lg-2-5,.pure-u-lg-3-5,.pure-u-lg-4-5,.pure-u-lg-5-5,.pure-u-lg-1-6,.pure-u-lg-5-6,.pure-u-lg-1-8,.pure-u-lg-3-8,.pure-u-lg-5-8,.pure-u-lg-7-8,.pure-u-lg-1-12,.pure-u-lg-5-12,.pure-u-lg-7-12,.pure-u-lg-11-12,.pure-u-lg-1-24,.pure-u-lg-2-24,.pure-u-lg-3-24,.pure-u-lg-4-24,.pure-u-lg-5-24,.pure-u-lg-6-24,.pure-u-lg-7-24,.pure-u-lg-8-24,.pure-u-lg-9-24,.pure-u-lg-10-24,.pure-u-lg-11-24,.pure-u-lg-12-24,.pure-u-lg-13-24,.pure-u-lg-14-24,.pure-u-lg-15-24,.pure-u-lg-16-24,.pure-u-lg-17-24,.pure-u-lg-18-24,.pure-u-lg-19-24,.pure-u-lg-20-24,.pure-u-lg-21-24,.pure-u-lg-22-24,.pure-u-lg-23-24,.pure-u-lg-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-lg-1-24{width:4.1667%;*width:4.1357%}.pure-u-lg-1-12,.pure-u-lg-2-24{width:8.3333%;*width:8.3023%}.pure-u-lg-1-8,.pure-u-lg-3-24{width:12.5%;*width:12.469%}.pure-u-lg-1-6,.pure-u-lg-4-24{width:16.6667%;*width:16.6357%}.pure-u-lg-1-5{width:20%;*width:19.969%}.pure-u-lg-5-24{width:20.8333%;*width:20.8023%}.pure-u-lg-1-4,.pure-u-lg-6-24{width:25%;*width:24.969%}.pure-u-lg-7-24{width:29.1667%;*width:29.1357%}.pure-u-lg-1-3,.pure-u-lg-8-24{width:33.3333%;*width:33.3023%}.pure-u-lg-3-8,.pure-u-lg-9-24{width:37.5%;*width:37.469%}.pure-u-lg-2-5{width:40%;*width:39.969%}.pure-u-lg-5-12,.pure-u-lg-10-24{width:41.6667%;*width:41.6357%}.pure-u-lg-11-24{width:45.8333%;*width:45.8023%}.pure-u-lg-1-2,.pure-u-lg-12-24{width:50%;*width:49.969%}.pure-u-lg-13-24{width:54.1667%;*width:54.1357%}.pure-u-lg-7-12,.pure-u-lg-14-24{width:58.3333%;*width:58.3023%}.pure-u-lg-3-5{width:60%;*width:59.969%}.pure-u-lg-5-8,.pure-u-lg-15-24{width:62.5%;*width:62.469%}.pure-u-lg-2-3,.pure-u-lg-16-24{width:66.6667%;*width:66.6357%}.pure-u-lg-17-24{width:70.8333%;*width:70.8023%}.pure-u-lg-3-4,.pure-u-lg-18-24{width:75%;*width:74.969%}.pure-u-lg-19-24{width:79.1667%;*width:79.1357%}.pure-u-lg-4-5{width:80%;*width:79.969%}.pure-u-lg-5-6,.pure-u-lg-20-24{width:83.3333%;*width:83.3023%}.pure-u-lg-7-8,.pure-u-lg-21-24{width:87.5%;*width:87.469%}.pure-u-lg-11-12,.pure-u-lg-22-24{width:91.6667%;*width:91.6357%}.pure-u-lg-23-24{width:95.8333%;*width:95.8023%}.pure-u-lg-1,.pure-u-lg-1-1,.pure-u-lg-5-5,.pure-u-lg-24-24{width:100%}}@media screen and (min-width:80em){.pure-u-xl-1,.pure-u-xl-1-1,.pure-u-xl-1-2,.pure-u-xl-1-3,.pure-u-xl-2-3,.pure-u-xl-1-4,.pure-u-xl-3-4,.pure-u-xl-1-5,.pure-u-xl-2-5,.pure-u-xl-3-5,.pure-u-xl-4-5,.pure-u-xl-5-5,.pure-u-xl-1-6,.pure-u-xl-5-6,.pure-u-xl-1-8,.pure-u-xl-3-8,.pure-u-xl-5-8,.pure-u-xl-7-8,.pure-u-xl-1-12,.pure-u-xl-5-12,.pure-u-xl-7-12,.pure-u-xl-11-12,.pure-u-xl-1-24,.pure-u-xl-2-24,.pure-u-xl-3-24,.pure-u-xl-4-24,.pure-u-xl-5-24,.pure-u-xl-6-24,.pure-u-xl-7-24,.pure-u-xl-8-24,.pure-u-xl-9-24,.pure-u-xl-10-24,.pure-u-xl-11-24,.pure-u-xl-12-24,.pure-u-xl-13-24,.pure-u-xl-14-24,.pure-u-xl-15-24,.pure-u-xl-16-24,.pure-u-xl-17-24,.pure-u-xl-18-24,.pure-u-xl-19-24,.pure-u-xl-20-24,.pure-u-xl-21-24,.pure-u-xl-22-24,.pure-u-xl-23-24,.pure-u-xl-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-xl-1-24{width:4.1667%;*width:4.1357%}.pure-u-xl-1-12,.pure-u-xl-2-24{width:8.3333%;*width:8.3023%}.pure-u-xl-1-8,.pure-u-xl-3-24{width:12.5%;*width:12.469%}.pure-u-xl-1-6,.pure-u-xl-4-24{width:16.6667%;*width:16.6357%}.pure-u-xl-1-5{width:20%;*width:19.969%}.pure-u-xl-5-24{width:20.8333%;*width:20.8023%}.pure-u-xl-1-4,.pure-u-xl-6-24{width:25%;*width:24.969%}.pure-u-xl-7-24{width:29.1667%;*width:29.1357%}.pure-u-xl-1-3,.pure-u-xl-8-24{width:33.3333%;*width:33.3023%}.pure-u-xl-3-8,.pure-u-xl-9-24{width:37.5%;*width:37.469%}.pure-u-xl-2-5{width:40%;*width:39.969%}.pure-u-xl-5-12,.pure-u-xl-10-24{width:41.6667%;*width:41.6357%}.pure-u-xl-11-24{width:45.8333%;*width:45.8023%}.pure-u-xl-1-2,.pure-u-xl-12-24{width:50%;*width:49.969%}.pure-u-xl-13-24{width:54.1667%;*width:54.1357%}.pure-u-xl-7-12,.pure-u-xl-14-24{width:58.3333%;*width:58.3023%}.pure-u-xl-3-5{width:60%;*width:59.969%}.pure-u-xl-5-8,.pure-u-xl-15-24{width:62.5%;*width:62.469%}.pure-u-xl-2-3,.pure-u-xl-16-24{width:66.6667%;*width:66.6357%}.pure-u-xl-17-24{width:70.8333%;*width:70.8023%}.pure-u-xl-3-4,.pure-u-xl-18-24{width:75%;*width:74.969%}.pure-u-xl-19-24{width:79.1667%;*width:79.1357%}.pure-u-xl-4-5{width:80%;*width:79.969%}.pure-u-xl-5-6,.pure-u-xl-20-24{width:83.3333%;*width:83.3023%}.pure-u-xl-7-8,.pure-u-xl-21-24{width:87.5%;*width:87.469%}.pure-u-xl-11-12,.pure-u-xl-22-24{width:91.6667%;*width:91.6357%}.pure-u-xl-23-24{width:95.8333%;*width:95.8023%}.pure-u-xl-1,.pure-u-xl-1-1,.pure-u-xl-5-5,.pure-u-xl-24-24{width:100%}}
--------------------------------------------------------------------------------
/api/css/vex.css:
--------------------------------------------------------------------------------
1 | @keyframes vex-fadein {
2 | 0% {
3 | opacity: 0; }
4 | 100% {
5 | opacity: 1; } }
6 |
7 | @-webkit-keyframes vex-fadein {
8 | 0% {
9 | opacity: 0; }
10 | 100% {
11 | opacity: 1; } }
12 |
13 | @-moz-keyframes vex-fadein {
14 | 0% {
15 | opacity: 0; }
16 | 100% {
17 | opacity: 1; } }
18 |
19 | @-ms-keyframes vex-fadein {
20 | 0% {
21 | opacity: 0; }
22 | 100% {
23 | opacity: 1; } }
24 |
25 | @-o-keyframes vex-fadein {
26 | 0% {
27 | opacity: 0; }
28 | 100% {
29 | opacity: 1; } }
30 |
31 | @keyframes vex-fadeout {
32 | 0% {
33 | opacity: 1; }
34 | 100% {
35 | opacity: 0; } }
36 |
37 | @-webkit-keyframes vex-fadeout {
38 | 0% {
39 | opacity: 1; }
40 | 100% {
41 | opacity: 0; } }
42 |
43 | @-moz-keyframes vex-fadeout {
44 | 0% {
45 | opacity: 1; }
46 | 100% {
47 | opacity: 0; } }
48 |
49 | @-ms-keyframes vex-fadeout {
50 | 0% {
51 | opacity: 1; }
52 | 100% {
53 | opacity: 0; } }
54 |
55 | @-o-keyframes vex-fadeout {
56 | 0% {
57 | opacity: 1; }
58 | 100% {
59 | opacity: 0; } }
60 |
61 | @keyframes vex-rotation {
62 | 0% {
63 | transform: rotate(0deg);
64 | -webkit-transform: rotate(0deg);
65 | -moz-transform: rotate(0deg);
66 | -ms-transform: rotate(0deg);
67 | -o-transform: rotate(0deg); }
68 | 100% {
69 | transform: rotate(359deg);
70 | -webkit-transform: rotate(359deg);
71 | -moz-transform: rotate(359deg);
72 | -ms-transform: rotate(359deg);
73 | -o-transform: rotate(359deg); } }
74 |
75 | @-webkit-keyframes vex-rotation {
76 | 0% {
77 | transform: rotate(0deg);
78 | -webkit-transform: rotate(0deg);
79 | -moz-transform: rotate(0deg);
80 | -ms-transform: rotate(0deg);
81 | -o-transform: rotate(0deg); }
82 | 100% {
83 | transform: rotate(359deg);
84 | -webkit-transform: rotate(359deg);
85 | -moz-transform: rotate(359deg);
86 | -ms-transform: rotate(359deg);
87 | -o-transform: rotate(359deg); } }
88 |
89 | @-moz-keyframes vex-rotation {
90 | 0% {
91 | transform: rotate(0deg);
92 | -webkit-transform: rotate(0deg);
93 | -moz-transform: rotate(0deg);
94 | -ms-transform: rotate(0deg);
95 | -o-transform: rotate(0deg); }
96 | 100% {
97 | transform: rotate(359deg);
98 | -webkit-transform: rotate(359deg);
99 | -moz-transform: rotate(359deg);
100 | -ms-transform: rotate(359deg);
101 | -o-transform: rotate(359deg); } }
102 |
103 | @-ms-keyframes vex-rotation {
104 | 0% {
105 | transform: rotate(0deg);
106 | -webkit-transform: rotate(0deg);
107 | -moz-transform: rotate(0deg);
108 | -ms-transform: rotate(0deg);
109 | -o-transform: rotate(0deg); }
110 | 100% {
111 | transform: rotate(359deg);
112 | -webkit-transform: rotate(359deg);
113 | -moz-transform: rotate(359deg);
114 | -ms-transform: rotate(359deg);
115 | -o-transform: rotate(359deg); } }
116 |
117 | @-o-keyframes vex-rotation {
118 | 0% {
119 | transform: rotate(0deg);
120 | -webkit-transform: rotate(0deg);
121 | -moz-transform: rotate(0deg);
122 | -ms-transform: rotate(0deg);
123 | -o-transform: rotate(0deg); }
124 | 100% {
125 | transform: rotate(359deg);
126 | -webkit-transform: rotate(359deg);
127 | -moz-transform: rotate(359deg);
128 | -ms-transform: rotate(359deg);
129 | -o-transform: rotate(359deg); } }
130 |
131 | .vex, .vex *, .vex *:before, .vex *:after {
132 | -moz-box-sizing: border-box;
133 | -webkit-box-sizing: border-box;
134 | box-sizing: border-box; }
135 |
136 | .vex {
137 | position: fixed;
138 | overflow: auto;
139 | -webkit-overflow-scrolling: touch;
140 | z-index: 1111;
141 | top: 0;
142 | right: 0;
143 | bottom: 0;
144 | left: 0; }
145 |
146 | .vex-scrollbar-measure {
147 | position: absolute;
148 | top: -9999px;
149 | width: 50px;
150 | height: 50px;
151 | overflow: scroll; }
152 |
153 | .vex-overlay {
154 | background: #000;
155 | filter: alpha(opacity=40);
156 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; }
157 |
158 | .vex-overlay {
159 | animation: vex-fadein 0.5s;
160 | -webkit-animation: vex-fadein 0.5s;
161 | -moz-animation: vex-fadein 0.5s;
162 | -ms-animation: vex-fadein 0.5s;
163 | -o-animation: vex-fadein 0.5s;
164 | -webkit-backface-visibility: hidden;
165 | position: fixed;
166 | background: rgba(0, 0, 0, 0.4);
167 | top: 0;
168 | right: 0;
169 | bottom: 0;
170 | left: 0; }
171 |
172 | .vex-close:before {
173 | font-family: Arial, sans-serif;
174 | content: "\00D7"; }
175 |
176 | .vex-dialog-form {
177 | margin: 0; }
178 |
179 | .vex-dialog-button {
180 | text-rendering: optimizeLegibility;
181 | -moz-appearance: none;
182 | -webkit-appearance: none;
183 | cursor: pointer;
184 | -webkit-tap-highlight-color: transparent; }
185 |
186 | .vex-loading-spinner {
187 | animation: vex-rotation 0.7s linear infinite;
188 | -webkit-animation: vex-rotation 0.7s linear infinite;
189 | -moz-animation: vex-rotation 0.7s linear infinite;
190 | -ms-animation: vex-rotation 0.7s linear infinite;
191 | -o-animation: vex-rotation 0.7s linear infinite;
192 | -webkit-backface-visibility: hidden;
193 | -moz-box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
194 | -webkit-box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
195 | box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
196 | position: fixed;
197 | z-index: 1112;
198 | margin: auto;
199 | top: 0;
200 | right: 0;
201 | bottom: 0;
202 | left: 0;
203 | height: 2em;
204 | width: 2em;
205 | background: #fff; }
206 |
207 | body.vex-open {
208 | overflow: hidden; }
209 |
--------------------------------------------------------------------------------
/api/img/gym_NEUTRAL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/gym_NEUTRAL.png
--------------------------------------------------------------------------------
/api/img/gym_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/gym_blue.png
--------------------------------------------------------------------------------
/api/img/gym_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/gym_red.png
--------------------------------------------------------------------------------
/api/img/gym_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/gym_yellow.png
--------------------------------------------------------------------------------
/api/img/license.txt:
--------------------------------------------------------------------------------
1 | https://shareicon.net/license/cc-3-0-by
--------------------------------------------------------------------------------
/api/img/pokestop_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/pokestop_blue.png
--------------------------------------------------------------------------------
/api/img/pokestop_lure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/pokestop_lure.png
--------------------------------------------------------------------------------
/api/img/pokestop_puple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/pokestop_puple.png
--------------------------------------------------------------------------------
/api/img/spawn_point.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maierfelix/POGOserver/0f73b8805e9dd5e9a1ec309fa1e1f07361689943/api/img/spawn_point.png
--------------------------------------------------------------------------------
/api/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | POGOserver api
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Ping: 0ms
21 |
22 |
23 |
POGOserver
24 |
25 |
26 |
27 |
28 |
29 |
Login
30 |
31 |
32 |
33 |
34 |
35 |
36 |
Server version: v0.1.0
37 |
Connected players: 0
38 |
World Manager
39 |
40 |
41 |
42 |
43 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/api/index.js:
--------------------------------------------------------------------------------
1 | import http from "http";
2 | import url from "url";
3 | import path from "path";
4 | import fs from "fs";
5 | const port = process.argv[2] || 9000;
6 |
7 | http.createServer((request, response) => {
8 | const uri = url.parse(request.url).pathname;
9 | let filename = path.join(process.cwd(), uri);
10 |
11 | fs.exists(filename, exists => {
12 | if(!exists) {
13 | response.writeHead(404, {"Content-Type": "text/plain"});
14 | response.write("404 Not Found\n");
15 | response.end();
16 | return;
17 | }
18 |
19 | if (fs.statSync(filename).isDirectory()) filename += '/api/index.html';
20 |
21 | fs.readFile(filename, "binary", (err, file) => {
22 | if(err) {
23 | response.writeHead(500, {"Content-Type": "text/plain"});
24 | response.write(`${err}\n`);
25 | response.end();
26 | return;
27 | }
28 |
29 | response.writeHead(200);
30 | response.write(file, "binary");
31 | response.end();
32 | });
33 | });
34 | }).listen(parseInt(port, 10));
35 |
36 | console.log(`Web-API for POGOserver running at => http://localhost:${port}/\nCTRL + C to shutdown`);
--------------------------------------------------------------------------------
/api/js/ajax.js:
--------------------------------------------------------------------------------
1 | function send(data, resolve) {
2 | const xhr = new XMLHttpRequest();
3 | const protocol = window.location.protocol;
4 | xhr.open("POST", `${protocol}//${CFG.API.HOST}:${CFG.API.PORT}${CFG.API.ROUTE}`, true);
5 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
6 | xhr.onreadystatechange = () => {
7 | if (xhr.readyState === 4) {
8 | if (xhr.status === 200) {
9 | if (typeof resolve === "function") {
10 | try {
11 | resolve(JSON.parse(xhr.responseText));
12 | } catch (e) {
13 | resolve(void 0);
14 | }
15 | }
16 | } else {
17 | resolve(xhr.statusText);
18 | }
19 | }
20 | };
21 | xhr.send(JSON.stringify(data));
22 | }
--------------------------------------------------------------------------------
/api/js/init.js:
--------------------------------------------------------------------------------
1 | ((() => {
2 |
3 | function loadScriptDefered(src) {
4 | let js = null;
5 | js = document.createElement("script");
6 | js.type = "text/javascript";
7 | js.src = src;
8 | js.async = false;
9 | document.body.appendChild(js);
10 | };
11 |
12 | loadScriptDefered(`http://maps.google.com/maps/api/js?key=${CFG.GMAPS.API_KEY}`);
13 | loadScriptDefered("/api/js/gmaps.js");
14 | loadScriptDefered("/api/js/ajax.js");
15 | loadScriptDefered("/api/js/main.js");
16 |
17 | }))();
--------------------------------------------------------------------------------
/api/js/main.js:
--------------------------------------------------------------------------------
1 | ((() => {
2 |
3 | let loggedIn = false;
4 | let loginTimeout = null;
5 |
6 | let heartInterval = null;
7 | let heartTimeout = null;
8 | let heartTimedOut = true;
9 |
10 | const header = `
11 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
63 | `;
64 |
65 | const gmap = new GMaps({
66 | el: "#map",
67 | disableDoubleClickZoom: true,
68 | lat: 0,
69 | lng: 0,
70 | disableDefaultUI: true,
71 | dblclick(e) {
72 | vex.dialog.open({
73 | message: "",
74 | input: header,
75 | buttons: [
76 | $.extend({}, vex.dialog.buttons.YES, {
77 | text: "Submit"
78 | }),
79 | $.extend({}, vex.dialog.buttons.NO, {
80 | text: "Abort"
81 | })
82 | ],
83 | callback(data) {
84 | if (data !== false && Object.keys(data).length) {
85 | const ed = e.data = {};
86 | if (data.type === "SPAWN") {
87 | ed.interval = data.interval;
88 | ed.encounters = data.encounters;
89 | }
90 | else if (data.type === "CHECKPOINT") {
91 | ed.name = data.name;
92 | ed.description = data.description;
93 | ed.imageUrl = data.image_url;
94 | ed.experience = data.experience;
95 | }
96 | else if (data.type === "GYM") {
97 | ed.team = data.team;
98 | }
99 | e.type = data.type;
100 | addFort(e, ed);
101 | }
102 | }
103 | })
104 | }
105 | });
106 |
107 | function addFort(e, data) {
108 | let lat = e.latLng.lat();
109 | let lng = e.latLng.lng();
110 | let obj = {
111 | action: "addFortToPosition",
112 | latitude: lat,
113 | longitude: lng,
114 | zoom: gmap.zoom,
115 | type: e.type
116 | };
117 | Object.assign(obj, data);
118 | send(obj, res => {
119 | console.log(res);
120 | refreshMapForts();
121 | });
122 | }
123 |
124 | function setStatus(txt, color) {
125 | connection_status.innerHTML = txt;
126 | connection_status.style.color = color;
127 | }
128 |
129 | setStatus("Connecting", "yellow");
130 |
131 | send({
132 | action: "init"
133 | }, res => {
134 | if (res.success) {
135 | setStatus("Connected!", "green");
136 | }
137 | else {
138 | if (res.reason !== void 0) {
139 | setStatus(res.reason);
140 | } else {
141 | setStatus("Connection failed!", "red");
142 | }
143 | return void 0;
144 | }
145 | });
146 |
147 | login_attempt.addEventListener("click", login);
148 |
149 | submit_spawn.addEventListener("click", () => {
150 | send({
151 | action: "spawnPkmnToPlayer",
152 | player: spawn_user.value,
153 | pkmn: spawn_pkmn.value
154 | }, res => {
155 | console.log(res);
156 | });
157 | });
158 |
159 | function login() {
160 |
161 | const username = login_username.value;
162 | const password = login_password.value;
163 |
164 | send({
165 | action: "login",
166 | username,
167 | password
168 | }, res => {
169 | if (res.success) {
170 | afterLogin();
171 | }
172 | else {
173 | setStatus("Login failed!", "red");
174 | clearTimeout(loginTimeout);
175 | loginTimeout = setTimeout(() => {
176 | if (loggedIn) {
177 | setStatus("Connected!", "green");
178 | }
179 | }, 3e3);
180 | }
181 | });
182 |
183 | }
184 |
185 | function afterLogin() {
186 | loggedIn = true;
187 | login_area.style.display = "none";
188 | setStatus("Logged in!", "green");
189 | world_manager.style.display = "block";
190 | server_ping.style.display = "block";
191 | map_manager.style.display = "block";
192 | gmap.refresh();
193 | gmap.setCenter({
194 | lat: CFG.GMAPS.BASE_LAT,
195 | lng: CFG.GMAPS.BASE_LNG
196 | });
197 | gmap.setZoom(CFG.GMAPS.BASE_ZOOM);
198 | initHeartBeat();
199 | refreshMapForts();
200 | refreshConnectedPlayers();
201 | getServerVersion();
202 | }
203 |
204 | function refreshConnectedPlayers() {
205 | send({
206 | action: "getConnectedPlayers"
207 | }, res => {
208 | connected_players.innerHTML = `Connected players: ${res.connected_players}`;
209 | });
210 | }
211 |
212 | function getServerVersion() {
213 | send({
214 | action: "getServerVersion"
215 | }, res => {
216 | server_version.innerHTML = `Server version: v${res.version}`;
217 | });
218 | }
219 |
220 | function getFortIcon(fort) {
221 | if (fort.type === "CHECKPOINT") return ("/api/img/pokestop_blue.png");
222 | else if (fort.uid[fort.uid.length - 1] === "S") return ("/api/img/spawn_point.png");
223 | else return (`/api/img/gym_${fort.owned_by_team}.png`);
224 | }
225 |
226 | function refreshMapForts() {
227 | let center = gmap.getCenter();
228 | let lat = center.lat();
229 | let lng = center.lng();
230 | send({
231 | action: "getFortsByPosition",
232 | latitude: lat,
233 | longitude: lng,
234 | zoom: gmap.zoom
235 | }, result => {
236 | gmap.removeMarkers();
237 | result.forts.map((fort) => {
238 | let icon = getFortIcon(fort);
239 | gmap.addMarker({
240 | lat: fort.latitude,
241 | lng: fort.longitude,
242 | title: fort.name,
243 | icon,
244 | rightclick: function(e) {
245 | vex.dialog.confirm({
246 | message: `})
Delete this fort?`,
247 | callback: function(value) {
248 | if (value) removeFort(this);
249 | }.bind(fort)
250 | })
251 | }.bind(fort)
252 | });
253 | });
254 | });
255 | }
256 |
257 | function removeFort(fort) {
258 | send({
259 | action: "deleteFortById",
260 | uid: fort.uid,
261 | latitude: fort.latitude,
262 | longitude: fort.longitude,
263 | zoom: gmap.zoom
264 | }, res => {
265 | console.log(res);
266 | refreshMapForts();
267 | });
268 | }
269 |
270 | function initHeartBeat() {
271 | clearInterval(heartInterval);
272 | heartInterval = setInterval(() => {
273 | heartTimedOut = true;
274 | const now = +new Date();
275 | heartTimeout = setTimeout(() => {
276 | if (heartTimedOut) {
277 | console.error("Heartbeat timeout!");
278 | loggedIn = false;
279 | setStatus("Reconnecting..", "yellow");
280 | login();
281 | }
282 | }, 5e3);
283 | send({
284 | action: "heartBeat",
285 | timestamp: now
286 | }, res => {
287 | if (res.timestamp) {
288 | heartTimedOut = false;
289 | clearTimeout(heartTimeout);
290 | const ping = res.timestamp - now;
291 | server_ping.innerHTML = `Ping: ${ping}ms`;
292 | refreshConnectedPlayers();
293 | refreshMapForts();
294 | }
295 | });
296 | }, 3e3);
297 | }
298 |
299 | }))();
--------------------------------------------------------------------------------
/cfg.js.example:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 |
3 | export default {
4 |
5 | VERSION: JSON.parse(fs.readFileSync("./package.json")).version,
6 |
7 | // show greeting
8 | GREET: true,
9 |
10 | // Api things
11 | API_ENABLE: true,
12 | API_USERNAME: "root",
13 | API_PASSWORD: "",
14 | API_ALLOWED_HOSTS: ["localhost", "127.0.0.1"],
15 |
16 | // Server settings
17 | MAX_CONNECTIONS: 64,
18 | PORT: 3000,
19 | // If using vmware, vps or multiple network adapters, set the related ip here
20 | // otherwise leave it blank
21 | LOCAL_IP: "",
22 | GAME_MODE: 0,
23 | SAVE_INTERVAL: 1e4,
24 | // Better dont touch these
25 | TICK_INTERVAL: 1,
26 | // Timeouts
27 | BOOT_TIMEOUT: 1e4,
28 | PLAYER_CONNECTION_TIMEOUT: 1e3 * 60 * 30,
29 | CELL_TIMEOUT: 1e3 * 60,
30 | MINIMUM_CLIENT_VERSION: "0.35.0",
31 | DEFAULT_CONSOLE_COLOR: 32,
32 | TRANSFER_ACCOUNTS: false,
33 |
34 | // Server debug options
35 | DEBUG_DUMP_PATH: "logs/",
36 | DEBUG_DUMP_TRAFFIC: true,
37 | DEBUG_LOG_REQUESTS: true,
38 |
39 | // Choose a database type
40 | DATABASE_TYPE: "MYSQL",
41 |
42 | // MySQL credentials
43 | MYSQL_PORT: 3306,
44 | MYSQL_HOST_IP: "127.0.0.1",
45 | MYSQL_DB_NAME: "pogosql",
46 | MYSQL_USERNAME: "root",
47 | MYSQL_PASSWORD: "",
48 | MYSQL_GYM_TABLE: "gym",
49 | MYSQL_USERS_TABLE: "users",
50 | MYSQL_SPAWN_TABLE: "spawn_points",
51 | MYSQL_POKESTOP_TABLE: "pokestop",
52 | MYSQL_OWNED_PKMN_TABLE: "owned_pkmn",
53 |
54 | // Used for asset download session
55 | DOWNLOAD_PROVIDER: "GOOGLE",
56 | DOWNLOAD_USERNAME: "USERNAME",
57 | DOWNLOAD_PASSWORD: "PASSWORD",
58 |
59 | // Google maps api key
60 | GMAPS_KEY: "AIzaSyDF9rkP8lhcddBtvH9gVFzjnNo13WtmJIM",
61 |
62 | // Currently supported pokemon
63 | MAX_POKEMON_NATIONAL_ID: 151,
64 | DUMP_ASSET_PATH: "data/"
65 |
66 | }
--------------------------------------------------------------------------------
/install-windows.bat:
--------------------------------------------------------------------------------
1 | set LIBPROTOBUF=%CD%\protobuf
2 | npm install node-protobuf && npm install
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "POGOServer",
3 | "version": "0.6.1",
4 | "description": "",
5 | "repository": {
6 | "type": "git",
7 | "url": "git://github.com/maierfelix/POGOServer.git"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\"",
11 | "babel-node": "babel-node --presets=es2015",
12 | "boot": "npm run babel-node -- ./src/index.js",
13 | "api": "npm run babel-node -- ./api/index.js",
14 | "update": "npm run babel-node -- ./updater.js"
15 | },
16 | "engines": {
17 | "node": ">= 6.x",
18 | "npm": ">= 3.x"
19 | },
20 | "author": "Felix Maier",
21 | "license": "GNU GPL v3",
22 | "dependencies": {
23 | "babel-cli": "^6.11.4",
24 | "babel-preset-es2015": "^6.13.1",
25 | "babel-preset-stage-0": "^6.5.0",
26 | "directory-tree": "^1.1.1",
27 | "fs-extra": "^0.30.0",
28 | "jwt-decode": "^2.1.0",
29 | "mysql": "^2.11.1",
30 | "nodegit": "^0.16.0",
31 | "node-pogo-protos": "^1.4.0",
32 | "pcrypt": "git+https://github.com/laverdet/pcrypt.git",
33 | "pogo-asset-downloader": "^0.3.1",
34 | "pokerare": "^0.1.1",
35 | "pokemongo-protobuf": "^1.11.0",
36 | "prompt": "^1.0.0",
37 | "s2-geometry": "^1.2.9",
38 | "url": "^0.11.0"
39 | },
40 | "devDependencies": {}
41 | }
42 |
--------------------------------------------------------------------------------
/src/api.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import url from "url";
3 | import s2 from "s2-geometry";
4 |
5 | import Cell from "./models/World/Cell";
6 |
7 | import print from "./print";
8 | import CFG from "../cfg";
9 |
10 | import {
11 | getHashCodeFrom
12 | } from "./utils";
13 |
14 | const S2Geo = s2.S2;
15 |
16 | let showHint = true;
17 |
18 | export function processApiCall(req, res, route) {
19 |
20 | let allowedHosts = CFG.API_ALLOWED_HOSTS;
21 |
22 | let hoster = url.parse(req.headers.referer).host;
23 |
24 | if (!(allowedHosts.indexOf(hoster) > -1)) {
25 | print(`Denied API access for ${hoster}!`, 31);
26 | if (showHint) {
27 | print(`To grant ${hoster} API access, add it to the allowed hosts in cfg.js`, 33);
28 | showHint = false;
29 | }
30 | let result = {
31 | success:false,
32 | reason: "API access denied!"
33 | };
34 | this.answerApiCall(res, JSON.stringify(result));
35 | return void 0;
36 | }
37 |
38 | let raw = req.body.toString();
39 | let json = null;
40 |
41 | try {
42 | json = JSON.parse(raw);
43 | } catch (e) {
44 | print(e, 31);
45 | this.answerApiCall(res, "");
46 | return void 0;
47 | }
48 |
49 | if (this.isApiCall(json)) {
50 | json.host = hoster;
51 | if (json.action === "login") {
52 | this["api_login"](json).then((result) => {
53 | this.answerApiCall(res, JSON.stringify(result));
54 | });
55 | }
56 | else {
57 | if (this.apiClients[hoster]) {
58 | this["api_" + json.action](json).then((result) => {
59 | this.answerApiCall(res, JSON.stringify(result));
60 | });
61 | }
62 | else {
63 | print(`${hoster} isnt logged in! Kicking..`, 31);
64 | }
65 | }
66 | }
67 | else {
68 | if (json.action === "init") {
69 | this.answerApiCall(res, JSON.stringify({ success: true }));
70 | }
71 | }
72 |
73 | }
74 |
75 | export function answerApiCall(res, data) {
76 | res.setHeader("Access-Control-Allow-Origin", "*");
77 | res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE");
78 | res.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type");
79 | res.setHeader("Access-Control-Allow-Credentials", true);
80 | res.end(data);
81 | }
82 |
83 | export function api_login(data) {
84 |
85 | if (typeof data !== "object") return void 0;
86 |
87 | let success = false;
88 |
89 | let username = CFG.API_USERNAME;
90 | let password = CFG.API_PASSWORD;
91 |
92 | if (
93 | username === data.username &&
94 | password === data.password
95 | ) {
96 | success = true;
97 | if (!this.apiClients[data.host]) {
98 | print(`API access for ${data.host} granted!`);
99 | }
100 | print(`${data.host} logged in!`, 36);
101 | this.apiClients[data.host] = {
102 | timestamp: +new Date()
103 | };
104 | }
105 |
106 | return new Promise((resolve) => {
107 | resolve({
108 | success: success
109 | });
110 | });
111 |
112 | }
113 |
114 | export function api_heartBeat() {
115 | return new Promise((resolve) => {
116 | resolve({
117 | timestamp: +new Date()
118 | });
119 | });
120 | }
121 |
122 | export function api_getConnectedPlayers() {
123 | return new Promise((resolve) => {
124 | resolve({
125 | connected_players: this.world.connectedPlayers
126 | });
127 | });
128 | }
129 |
130 | export function api_getServerVersion() {
131 | return new Promise((resolve) => {
132 | resolve({
133 | version: CFG.VERSION
134 | });
135 | });
136 | }
137 |
138 | export function api_spawnPkmnToPlayer(data) {
139 | let name = String(data.player);
140 | let pkmn = String(data.pkmn).toUpperCase();
141 | print(`Spawned 1x ${pkmn}'s to ${name}!`);
142 | return new Promise((resolve) => {
143 | resolve({
144 | success: true
145 | });
146 | });
147 | }
148 |
149 | export function api_addFortToPosition(data) {
150 | return new Promise((resolve) => {
151 | this.world.insertFortIntoDatabase(data).then((fort) => {
152 | resolve({
153 | success: true
154 | });
155 | });
156 | });
157 | }
158 |
159 | export function api_deleteFortById(data) {
160 | let uid = data.uid;
161 | let cellId = Cell.getIdByPosition(data.latitude, data.longitude, data.zoom);
162 | return new Promise((resolve) => {
163 | this.world.deleteFort(cellId, uid).then(() => {
164 | resolve({
165 | id: cellId + "." + uid,
166 | success: true
167 | });
168 | });
169 | });
170 | }
171 |
172 | export function api_getFortsByPosition(data) {
173 | return new Promise((resolve) => {
174 | let lat = data.latitude;
175 | let lng = data.longitude;
176 | let zoom = data.zoom;
177 | this.getNeighboredForts(this.getNeighbors(lat, lng, zoom), [], 0).then((forts) => {
178 | let result = [];
179 | forts.map((fort) => {
180 | let fortData = fort.serialize();
181 | fortData.name = fort.name;
182 | fortData.uid = fort.uid;
183 | result.push(fortData);
184 | });
185 | resolve({
186 | forts: result,
187 | success: true
188 | });
189 | });
190 | });
191 | }
192 |
193 | export function getNeighbors(lat, lng, lvl) {
194 | let origin = S2Geo.latLngToKey(lat, lng, lvl);
195 | let walk = [S2Geo.keyToId(origin)];
196 | let next = S2Geo.nextKey(origin);
197 | let prev = S2Geo.prevKey(origin);
198 | let ii = 0;
199 | let length = 10;
200 | for (; ii < length; ++ii) {
201 | walk.push(S2Geo.toId(prev));
202 | walk.push(S2Geo.toId(next));
203 | next = S2Geo.nextKey(next);
204 | prev = S2Geo.prevKey(prev);
205 | };
206 | walk.sort((a, b) => a - b);
207 | return (walk);
208 | }
209 |
210 | export function getNeighboredForts(cells, out, index) {
211 | return new Promise((resolve) => {
212 | let id = cells[index];
213 | this.world.getFortsByCellId(id).then((cell) => {
214 | out = out.concat(cell.forts);
215 | if (++index >= cells.length) resolve(out);
216 | else resolve(this.getNeighboredForts(cells, out, index));
217 | });
218 | });
219 | }
--------------------------------------------------------------------------------
/src/commands.js:
--------------------------------------------------------------------------------
1 | import print from "./print";
2 | import CFG from "../cfg";
3 |
4 | export function saveAllPlayers() {
5 | if (this.world.players.length > 0) {
6 | for (let player of this.world.players) {
7 | this.savePlayer(player);
8 | };
9 | }
10 | }
11 |
12 | /**
13 | * @param {Player} player
14 | */
15 | export function savePlayer(player) {
16 | return new Promise((resolve) => {
17 | if (player.authenticated) {
18 | this.updateUser(player).then(resolve);
19 | }
20 | });
21 | }
22 |
23 | /**
24 | * @param {Player} player
25 | */
26 | export function updateUser(player) {
27 | let query = this.getUserQuery("UPDATE", "WHERE email=? LIMIT 1");
28 | let data = this.getUserQueryData(player);
29 |
30 | return new Promise((resolve) => {
31 | this.world.instance.db.query(query, data, (e, res) => {
32 | resolve();
33 | });
34 | });
35 | }
36 |
37 | /**
38 | * @param {Object} obj
39 | * @return {Array}
40 | */
41 | export function getUserQueryData(obj) {
42 | return ([
43 | obj.username,
44 | obj.email,
45 | obj.info._exp,
46 | obj.info._level,
47 | obj.info.stardust,
48 | obj.info.pokecoins,
49 | obj.info._team,
50 | // position
51 | obj.latitude,
52 | obj.longitude,
53 | obj.altitude,
54 | // contact settings
55 | 0, //obj.contact.sendMarketingEmails,
56 | 0, //obj.contact.sendPushNotifications,
57 | // inventory
58 | obj.candyBag.querify(), //obj.candyBag,
59 | obj.bag.querify(), //obj.bag,
60 | obj.avatar.querify(), //obj.avatar,
61 | '{"0":1,"1":1,"3":1,"4":1,"7":1}', //obj.tutorial,
62 | // WHERE
63 | obj.email
64 | ]);
65 | }
66 |
--------------------------------------------------------------------------------
/src/cycle.js:
--------------------------------------------------------------------------------
1 | import print from "./print";
2 | import CFG from "../cfg";
3 |
4 | import Settings from "./modes";
5 | const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds;
6 |
7 | export function startCycle() {
8 | this.cycleInstance = setTimeout(() => this.cycle(), CFG.TICK_INTERVAL);
9 | }
10 |
11 | export function stopCycle() {
12 | clearTimeout(this.cycleInstance);
13 | }
14 |
15 | export function cycle() {
16 |
17 | this.stopCycle();
18 | this.startCycle();
19 |
20 | if (this.STATES.PAUSE === true) return void 0;
21 | if (this.STATES.CRASH === true) return void 0;
22 |
23 | this.updateTimers();
24 |
25 | if (this.passedTicks <= 0) return void 0;
26 |
27 | this.resetTimers();
28 |
29 | return void 0;
30 |
31 | }
32 |
33 | export function updateTimers() {
34 | let local = Date.now();
35 | this.passedTicks = local - this.time;
36 | this.tick += this.passedTicks;
37 | this.time = local;
38 | return void 0;
39 | }
40 |
41 | export function resetTimers() {
42 | if (this.tick >= 1e4) {
43 | this.fullTick++;
44 | if (this.fullTick >= 2) {
45 | this.fullTick = 0;
46 | }
47 | this.tick = 0;
48 | // Timeout ticks, not precise
49 | this.playerTimeoutTick();
50 | this.cellTimeoutTick();
51 | }
52 | this.saveTick++;
53 | // Save interval
54 | if (this.saveTick >= CFG.SAVE_INTERVAL) {
55 | this.saveAllPlayers();
56 | this.saveTick = 0;
57 | }
58 | this.spawnTick++;
59 | // Pkmn spawn interval
60 | if (this.spawnTick >= MAP_REFRESH_RATE * 1e3) {
61 | this.world.refreshSpawns();
62 | this.spawnTick = 0;
63 | }
64 | return void 0;
65 | }
66 |
67 | export function playerTimeoutTick() {
68 | let maxTimeout = CFG.PLAYER_CONNECTION_TIMEOUT;
69 | let ii = 0;
70 | let length = this.world.connectedPlayers;
71 | let player = null;
72 | let players = this.world.players;
73 | for (; ii < length; ++ii) {
74 | player = players[ii];
75 | if (this.time - player.timeout >= maxTimeout) {
76 | print(`${player.remoteAddress} timed out`, 34);
77 | this.savePlayer(player);
78 | this.removePlayer(player);
79 | }
80 | };
81 | }
82 |
83 | export function cellTimeoutTick() {
84 | let ii = 0;
85 | let length = this.world.cells.length;
86 | let cell = null;
87 | for (; ii < length; ++ii) {
88 | cell = this.world.cells[ii];
89 | if (cell.expiration - +new Date() <= 0) {
90 | cell.delete();
91 | length--;
92 | }
93 | };
94 | }
--------------------------------------------------------------------------------
/src/db/create.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 |
3 | import print from "../print";
4 | import CFG from "../../cfg";
5 |
6 | export function createTableIfNotExists(name) {
7 | return new Promise((resolve) => {
8 | this.db.query(`SHOW TABLES LIKE '${name}';`, (e, rows, fields) => {
9 | if (e) print(e, 31);
10 | else {
11 | // exists
12 | if (rows && rows.length) resolve();
13 | // create user table
14 | else this.createTables().then(resolve);
15 | }
16 | });
17 | });
18 | }
19 |
20 | /**
21 | * @param {String} name
22 | */
23 | export function createTables() {
24 | return new Promise((resolve) => {
25 | this.createTable(CFG.MYSQL_GYM_TABLE).then(() => {
26 | this.createTable(CFG.MYSQL_USERS_TABLE).then(() => {
27 | this.createTable(CFG.MYSQL_SPAWN_TABLE).then(() => {
28 | this.createTable(CFG.MYSQL_POKESTOP_TABLE).then(() => {
29 | this.createTable(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => {
30 | resolve();
31 | });
32 | });
33 | });
34 | });
35 | });
36 | });
37 | }
38 |
39 | export function createTable(name) {
40 |
41 | print(`Creating table ${name}`, 36);
42 |
43 | let query = `
44 | CREATE TABLE IF NOT EXISTS ${name} (
45 | ${fs.readFileSync(__dirname + "/tables/" + name + ".table", "utf8")}
46 | ) ENGINE=InnoDB;
47 | `;
48 |
49 | return new Promise((resolve) => {
50 | this.db.query(query, (e, rows) => {
51 | if (e) print(e, 31);
52 | else resolve();
53 | });
54 | });
55 |
56 | }
--------------------------------------------------------------------------------
/src/db/get.js:
--------------------------------------------------------------------------------
1 | import print from "../print";
2 | import CFG from "../../cfg";
3 |
4 | /**
5 | * @param {String} column
6 | * @param {String} value
7 | * @param {String} table
8 | */
9 | export function getQueryByColumnFromTable(column, value, table) {
10 | return new Promise((resolve) => {
11 | this.db.query(`SELECT * FROM ${table} WHERE ${column}=?`, [value], (e, rows) => {
12 | if (e) print(e, 31);
13 | if (rows && rows.length) resolve(rows);
14 | else resolve(void 0);
15 | });
16 | });
17 | }
18 |
19 | /**
20 | * @param {String} column
21 | * @param {String} value
22 | * @param {String} table
23 | */
24 | export function deleteQueryByColumnFromTable(column, value, table) {
25 | return new Promise((resolve) => {
26 | this.db.query(`DELETE FROM ${table} WHERE ${column}=?`, [value], (e, rows) => {
27 | if (e) print(e, 31);
28 | else resolve(void 0);
29 | });
30 | });
31 | }
--------------------------------------------------------------------------------
/src/db/index.js:
--------------------------------------------------------------------------------
1 | import mysql from "mysql";
2 |
3 | import print from "../print";
4 | import CFG from "../../cfg";
5 |
6 | export function setupDatabaseConnection() {
7 |
8 | let connection = mysql.createConnection({
9 | host : CFG.MYSQL_HOST_IP,
10 | port : CFG.MYSQL_PORT,
11 | database : CFG.MYSQL_DB_NAME,
12 | user : CFG.MYSQL_USERNAME,
13 | password : CFG.MYSQL_PASSWORD
14 | });
15 |
16 | return new Promise((resolve) => {
17 | connection.connect((error) => {
18 | if (error) {
19 | print("MySQL " + error, 31);
20 | this.retry("Retrying again in ", () => this.setupDatabaseConnection().then(resolve), 5);
21 | return void 0;
22 | }
23 | this.db = connection;
24 | this.createTableIfNotExists(CFG.MYSQL_USERS_TABLE).then(() => {
25 | this.createTableIfNotExists(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => {
26 | print(`\x1b[36;1mMySQL\x1b[0m\x1b[32;1m connection established\x1b[0m`);
27 | resolve();
28 | });
29 | });
30 | });
31 | connection.on("error", (error) => {
32 | print("MySQL " + error, 31);
33 | this.retry("Trying to reconnect in ", () => this.setupDatabaseConnection().then(resolve), 5);
34 | });
35 | });
36 |
37 | }
38 |
39 | /**
40 | * @param {Function} resolve
41 | */
42 | export function closeConnection(resolve) {
43 | this.db.end(() => {
44 | resolve();
45 | });
46 | }
--------------------------------------------------------------------------------
/src/db/query.js:
--------------------------------------------------------------------------------
1 | import CFG from "../../cfg";
2 |
3 | /**
4 | * @return {String}
5 | */
6 | export function getUserQuery(cmd, after) {
7 | return (`
8 | ${cmd} ${CFG.MYSQL_USERS_TABLE}
9 | SET
10 | username=?,
11 | email=?,
12 | exp=?,
13 | level=?,
14 | stardust=?,
15 | pokecoins=?,
16 | team=?,
17 | latitude=?,
18 | longitude=?,
19 | altitude=?,
20 | send_marketing_emails=?,
21 | send_push_notifications=?,
22 | candies=?,
23 | items=?,
24 | avatar=?,
25 | tutorial=?
26 | ${after}
27 | `);
28 | }
29 |
30 | /**
31 | * @param {Player} player
32 | * @return {Array}
33 | */
34 | export function getPlayerQueryData(player) {
35 | return ([
36 | player.username,
37 | player.email,
38 | player.exp,
39 | player.level,
40 | player.stardust,
41 | player.pokecoins,
42 | player.team,
43 | // position
44 | player.latitude,
45 | player.longitude,
46 | player.altitude,
47 | // contact settings
48 | player.send_marketing_emails,
49 | player.send_push_notifications,
50 | // json
51 | player.candies.serialize(),
52 | player.items.serialize(),
53 | player.avatar.serialize(),
54 | player.tutorial.serialize()
55 | ]);
56 | }
57 |
58 | /**
59 | * @return {String}
60 | */
61 | export function getOwnedPkmnQuery(cmd, after) {
62 | return (`
63 | ${cmd} ${CFG.MYSQL_OWNED_PKMN_TABLE}
64 | SET
65 | owner_id=?,
66 | pokemon_id=?,
67 | cp=?,
68 | stamina=?,
69 | stamina_max=?,
70 | move_1=?,
71 | move_2=?,
72 | deployed_fort_id=?,
73 | is_egg=?,
74 | egg_km_walked_target=?,
75 | egg_km_walked_start=?,
76 | origin=?,
77 | height_m=?,
78 | weight_kg=?,
79 | individual_attack=?,
80 | individual_defense=?,
81 | individual_stamina=?,
82 | cp_multiplier=?,
83 | pokeball=?,
84 | captured_cell_id=?,
85 | battles_attacked=?,
86 | battles_defended=?,
87 | egg_incubator_id=?,
88 | creation_time_ms=?,
89 | num_upgrades=?,
90 | additional_cp_multiplier=?,
91 | favorite=?,
92 | nickname=?,
93 | from_fort=?
94 | ${after}
95 | `);
96 | }
97 |
98 | /**
99 | * @param {Object} obj
100 | * @return {Array}
101 | */
102 | export function getOwnedPkmnQueryData(obj) {
103 | return ([
104 | obj.owner_id,
105 | obj.pokemon_id,
106 | obj.cp,
107 | obj.stamina,
108 | obj.stamina_max,
109 | obj.move_1,
110 | obj.move_2,
111 | obj.deployed_fort_id || "",
112 | obj.is_egg || 0,
113 | obj.egg_km_walked_target || 0,
114 | obj.egg_km_walked_start || 0,
115 | obj.origin || 0,
116 | obj.height_m,
117 | obj.weight_kg,
118 | obj.individual_attack,
119 | obj.individual_defense,
120 | obj.individual_stamina,
121 | obj.cp_multiplier,
122 | obj.pokeball,
123 | obj.captured_cell_id || 0,
124 | obj.battles_attacked || 0,
125 | obj.battles_defended || 0,
126 | obj.egg_incubator_id || "",
127 | obj.creation_time_ms,
128 | obj.num_upgrades || 0,
129 | obj.additional_cp_multiplier || 0,
130 | obj.favorite || 0,
131 | obj.nickname || "",
132 | obj.from_fort || 0
133 | ]);
134 | }
--------------------------------------------------------------------------------
/src/db/tables/gym.table:
--------------------------------------------------------------------------------
1 | id int(11) NOT NULL AUTO_INCREMENT,
2 | cell_id varchar(64) NOT NULL,
3 | latitude double NOT NULL,
4 | longitude double NOT NULL,
5 | team int(1) NOT NULL,
6 | in_battle tinyint(1) NOT NULL DEFAULT '0',
7 | points int(11) NOT NULL DEFAULT '0',
8 | PRIMARY KEY (id)
--------------------------------------------------------------------------------
/src/db/tables/owned_pkmn.table:
--------------------------------------------------------------------------------
1 | id int(11) NOT NULL AUTO_INCREMENT,
2 | owner_id int(11) NOT NULL,
3 | dex_number int(11) NOT NULL,
4 | cp int(32) NOT NULL,
5 | stamina int(11) NOT NULL,
6 | stamina_max int(11) NOT NULL,
7 | move_1 varchar(32) NOT NULL,
8 | move_2 varchar(32) NOT NULL,
9 | deployed_fort_id longtext NULL DEFAULT NULL,
10 | is_egg tinyint(1) NULL DEFAULT NULL,
11 | egg_km_walked_target double NULL DEFAULT NULL,
12 | egg_km_walked_start double NULL DEFAULT NULL,
13 | origin int(11) NULL DEFAULT NULL,
14 | height_m double NOT NULL,
15 | weight_kg double NOT NULL,
16 | individual_attack int(11) NOT NULL,
17 | individual_defense int(11) NOT NULL,
18 | individual_stamina int(11) NOT NULL,
19 | cp_multiplier double NOT NULL,
20 | pokeball varchar(32) NOT NULL,
21 | captured_cell_id varchar(32) NULL DEFAULT NULL,
22 | battles_attacked int(11) NULL DEFAULT NULL,
23 | battles_defended int(11) NULL DEFAULT NULL,
24 | egg_incubator_id longtext NULL DEFAULT NULL,
25 | creation_time_ms bigint(20) NULL DEFAULT NULL,
26 | num_upgrades int(11) NULL DEFAULT NULL,
27 | additional_cp_multiplier double NULL DEFAULT NULL,
28 | favorite tinyint(1) NULL DEFAULT NULL,
29 | nickname longtext NULL DEFAULT NULL,
30 | from_fort int(11) NULL DEFAULT NULL,
31 | PRIMARY KEY (id)
--------------------------------------------------------------------------------
/src/db/tables/pokestop.table:
--------------------------------------------------------------------------------
1 | id int(11) NOT NULL AUTO_INCREMENT,
2 | cell_id varchar(64) NOT NULL,
3 | latitude double NOT NULL,
4 | longitude double NOT NULL,
5 | name varchar(64) NOT NULL,
6 | description varchar(128) NOT NULL,
7 | image_url varchar(128) NOT NULL,
8 | experience int(11) NOT NULL DEFAULT '0',
9 | rewards varchar(64) NOT NULL DEFAULT '{}',
10 | PRIMARY KEY (id)
--------------------------------------------------------------------------------
/src/db/tables/spawn_points.table:
--------------------------------------------------------------------------------
1 | id int(11) NOT NULL AUTO_INCREMENT,
2 | cell_id varchar(64) NOT NULL,
3 | latitude double NOT NULL DEFAULT '0',
4 | longitude double NOT NULL DEFAULT '0',
5 | encounters varchar(64) NOT NULL DEFAULT '{}',
6 | update_interval int(11) NOT NULL DEFAULT '5',
7 | min_spawn_expire int(11) NOT NULL DEFAULT '2',
8 | max_spawn_expire int(11) NOT NULL DEFAULT '15',
9 | PRIMARY KEY (id)
--------------------------------------------------------------------------------
/src/db/tables/users.table:
--------------------------------------------------------------------------------
1 | id int(11) NOT NULL AUTO_INCREMENT,
2 | username varchar(16) NOT NULL,
3 | email varchar(32) NOT NULL,
4 | exp int(11) NULL DEFAULT NULL,
5 | level smallint(11) NULL DEFAULT NULL,
6 | stardust int(11) NULL DEFAULT NULL,
7 | pokecoins int(11) NULL DEFAULT NULL,
8 | team tinyint(11) NULL DEFAULT NULL,
9 | latitude double NULL DEFAULT NULL,
10 | longitude double NULL DEFAULT NULL,
11 | altitude double NULL DEFAULT NULL,
12 | send_marketing_emails tinyint(1) NULL DEFAULT NULL,
13 | send_push_notifications tinyint(1) NULL DEFAULT NULL,
14 | candies varchar(1024) NOT NULL DEFAULT '{}',
15 | items varchar(255) NOT NULL DEFAULT '{}',
16 | avatar varchar(128) NOT NULL DEFAULT '{}',
17 | pokedex varchar(64) NOT NULL DEFAULT '{}',
18 | tutorial varchar(64) NOT NULL DEFAULT '{"0":1,"1":1,"3":1,"4":1,"7":1}',
19 | PRIMARY KEY (id)
20 |
--------------------------------------------------------------------------------
/src/dump.js:
--------------------------------------------------------------------------------
1 | import fse from "fs-extra";
2 | import POGOProtos from "pokemongo-protobuf";
3 |
4 | import print from "./print";
5 | import CFG from "../cfg";
6 |
7 | import { _toCC } from "./utils";
8 |
9 | /**
10 | * @param {Request} req
11 | * @param {Array} res
12 | * @return {Object}
13 | */
14 | export function decode(req, res) {
15 |
16 | // clone
17 | req = JSON.parse(JSON.stringify(req));
18 | res = JSON.parse(JSON.stringify(res));
19 |
20 | // dont decode unknown6, since it bloats the file size
21 | delete req.unknown6;
22 |
23 | // decode requests
24 | for (let request of req.requests) {
25 | let key = _toCC(request.request_type);
26 | let msg = request.request_message;
27 | if (msg) {
28 | let proto = `POGOProtos.Networking.Requests.Messages.${key}Message`;
29 | request.request_message = this.parseProtobuf(new Buffer(msg.data), proto);
30 | }
31 | };
32 |
33 | // decode responses
34 | let index = 0;
35 | for (let resp of res) {
36 | let key = _toCC(req.requests[index].request_type);
37 | let msg = new Buffer(resp);
38 | let proto = `POGOProtos.Networking.Responses.${key}Response`;
39 | res[index] = this.parseProtobuf(msg, proto);
40 | index++;
41 | };
42 |
43 | // clone again to build response out of it
44 | let req2 = JSON.parse(JSON.stringify(req));
45 |
46 | // build res base out of req
47 | delete req2.requests;
48 | req2.returns = res;
49 | req2.status_code = 1;
50 |
51 | return ({
52 | req: req,
53 | res: res
54 | });
55 |
56 | }
57 |
58 | /**
59 | * @param {Request} req
60 | * @param {Response} res
61 | */
62 | export function dumpTraffic(req, res) {
63 | try {
64 | let decoded = this.decode(req, res);
65 | let out = {
66 | Request: decoded.req,
67 | Response: decoded.res
68 | };
69 | decoded = JSON.stringify(out, null, 2);
70 | fse.outputFileSync(CFG.DEBUG_DUMP_PATH + Date.now(), decoded);
71 | } catch (e) {
72 | print("Dump traffic: " + e, 31);
73 | }
74 | }
--------------------------------------------------------------------------------
/src/enum.js:
--------------------------------------------------------------------------------
1 | import proto from "node-pogo-protos";
2 |
3 | export default {
4 | TEAM: proto.Enums.TeamColor,
5 | ITEMS: proto.Inventory.Item.ItemId,
6 | GENDER: proto.Enums.Gender,
7 | TUTORIAL: proto.Enums.TutorialState,
8 | POKEMON_IDS: proto.Enums.PokemonId,
9 | POKEMON_FAMILY: proto.Enums.PokemonFamilyId,
10 | getNameById: (emu, id) => {
11 | id <<= 0;
12 | for (let key in emu) {
13 | if (emu[key] === id) return (key);
14 | };
15 | return (null);
16 | },
17 | getIdByName: (emu, name) => {
18 | for (let key in emu) {
19 | if (key === name) return (emu[key]);
20 | };
21 | return (null);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/http.js:
--------------------------------------------------------------------------------
1 | import http from "http";
2 |
3 | import print from "./print";
4 | import CFG from "../cfg";
5 |
6 | /**
7 | * @return {HTTP}
8 | */
9 | export function createHTTPServer() {
10 | let server = http.createServer((req, res) => {
11 | if (this.world.isFull()) {
12 | print(`Server is full! Refused ${req.headers.host}`, 31);
13 | return void 0;
14 | }
15 | let chunks = [];
16 | req.on("data", (chunk) => {
17 | chunks.push(chunk);
18 | });
19 | req.on("end", () => {
20 | let buffer = Buffer.concat(chunks);
21 | req.body = buffer;
22 | this.routeRequest(req, res);
23 | });
24 | });
25 | server.listen(CFG.PORT);
26 | return (server);
27 | }
28 |
29 | export function shutdown() {
30 | this.socket.close(() => {
31 | print("Closed http server!", 33);
32 | this.closeConnection(() => {
33 | print("Closed database connection!", 33);
34 | print("Server shutdown!", 31);
35 | setTimeout(() => process.exit(1), 2e3);
36 | });
37 | });
38 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import os from "os";
3 | import request from "request";
4 | import readline from "readline";
5 | import POGOProtos from "pokemongo-protobuf";
6 |
7 | import {
8 | _toCC,
9 | deXOR,
10 | inherit,
11 | getHashCodeFrom
12 | } from "./utils";
13 |
14 | import print from "./print";
15 | import CFG from "../cfg";
16 |
17 | import World from "./models/World";
18 |
19 | import * as _api from "./api";
20 | import * as _commands from "./commands";
21 | import * as _dump from "./dump";
22 | import * as _http from "./http";
23 | import * as _setup from "./setup";
24 | import * as _cycle from "./cycle";
25 | import * as _request from "./request";
26 | import * as _response from "./response";
27 | import * as _process from "./process";
28 | import * as _mysql from "./db/index";
29 | import * as _mysql_get from "./db/get";
30 | import * as _mysql_query from "./db/query";
31 | import * as _mysql_create from "./db/create";
32 |
33 | const greetMessage = fs.readFileSync(".greet", "utf8");
34 |
35 | /**
36 | * @class GameServer
37 | */
38 | export default class GameServer {
39 |
40 | /** @constructor */
41 | constructor() {
42 |
43 | this.STATES = {
44 | PAUSE: false,
45 | DEBUG: false,
46 | CRASH: false
47 | };
48 |
49 | this.db = null;
50 |
51 | this.repository = null;
52 |
53 | this.apiClients = {};
54 |
55 | this.socket = null;
56 | this.cycleInstance = null;
57 |
58 | // Timer things
59 | this.tick = 0;
60 | this.time = 0;
61 | this.fullTick = 0;
62 | this.saveTick = 0;
63 | this.spawnTick = 0;
64 | this.timeoutTick = 0;
65 | this.passedTicks = 0;
66 |
67 | if (CFG.GREET) this.greet();
68 |
69 | this.getLatestVersion().then((latest) => {
70 | print(this.repository);
71 | let current = require("../package.json").version;
72 | print(`Booting Server v${current}`, 33);
73 | if (current < latest) {
74 | print(`WARNING: Please update to the latest build v${latest}!`, 33);
75 | }
76 | this.setup().then(() => {
77 | this.world = new World(this);
78 | });
79 | });
80 |
81 | }
82 |
83 | fetchVersioningUrl() {
84 | return new Promise((resolve) => {
85 | let url = "";
86 | let branch = "master";
87 | let base = "https://raw.githubusercontent.com";
88 | url = require("../package.json").repository.url;
89 | url = url.replace("git://", "");
90 | url = url.replace(".git", "");
91 | url = url.replace("github.com/", "");
92 | this.repository = `https://github.com/${url}`;
93 | url = `${base}/${url}/${branch}/package.json`;
94 | resolve(url);
95 | });
96 | }
97 |
98 | getLatestVersion() {
99 | return new Promise((resolve) => {
100 | this.fetchVersioningUrl().then((url) => {
101 | request({url: url}, (error, response, body) => {
102 | let json = null;
103 | try {
104 | json = JSON.parse(body);
105 | } catch (e) {
106 | json = {};
107 | print(e, 31);
108 | }
109 | resolve(json.version);
110 | });
111 | });
112 | });
113 | }
114 |
115 | /**
116 | * @param {Object} obj
117 | * @return {Boolean}
118 | */
119 | isApiCall(call) {
120 | let action = String(call.action);
121 | return (
122 | "api_" + action in _api
123 | );
124 | }
125 |
126 | /**
127 | * @param {String} msg
128 | * @param {Function} fn
129 | * @param {Number} timer
130 | */
131 | retry(msg, fn, timer) {
132 | process.stdout.clearLine();
133 | process.stdout.cursorTo(0);
134 | print(`${msg}${timer}s`, 33, true);
135 | if (timer >= 1) setTimeout(() => this.retry(msg, fn, --timer), 1e3);
136 | else {
137 | process.stdout.write("\n");
138 | fn();
139 | }
140 | }
141 |
142 | /**
143 | * @return {String}
144 | */
145 | getLocalIPv4() {
146 | let address = null;
147 | let interfaces = os.networkInterfaces();
148 | for (var dev in interfaces) {
149 | interfaces[dev].filter((details) => details.family === "IPv4" && details.internal === false ? address = details.address: void 0);
150 | };
151 | return (address);
152 | }
153 |
154 | /**
155 | * @param {Buffer} buffer
156 | * @param {String} schema
157 | */
158 | parseProtobuf(buffer, schema) {
159 | try {
160 | return POGOProtos.parseWithUnknown(buffer, schema);
161 | } catch (e) {
162 | print(e, 31);
163 | }
164 | }
165 |
166 | /**
167 | * @param {String} path
168 | * @return {Boolean}
169 | */
170 | fileExists(path) {
171 | try {
172 | fs.statSync(path);
173 | } catch (e) {
174 | return (false);
175 | }
176 | return (true);
177 | }
178 |
179 | /**
180 | * @return {String}
181 | */
182 | getCurrentTime() {
183 | let date = new Date();
184 | return (
185 | `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`
186 | );
187 | }
188 |
189 | greet() {
190 | console.log(greetMessage);
191 | }
192 |
193 | }
194 |
195 | inherit(GameServer, _api);
196 | inherit(GameServer, _commands);
197 | inherit(GameServer, _dump);
198 | inherit(GameServer, _http);
199 | inherit(GameServer, _setup);
200 | inherit(GameServer, _cycle);
201 | inherit(GameServer, _request);
202 | inherit(GameServer, _response);
203 | inherit(GameServer, _process);
204 | inherit(GameServer, _mysql);
205 | inherit(GameServer, _mysql_get);
206 | inherit(GameServer, _mysql_query);
207 | inherit(GameServer, _mysql_create);
208 |
209 | (() => {
210 |
211 | const server = new GameServer();
212 |
213 | const rl = readline.createInterface({
214 | input: process.stdin,
215 | output: process.stdout,
216 | terminal: false
217 | });
218 |
219 | rl.on("line", (data) => {
220 | server.stdinInput(data);
221 | });
222 |
223 | process.on("uncaughtException", (data) => {
224 | server.uncaughtException(data);
225 | });
226 |
227 | })();
228 |
--------------------------------------------------------------------------------
/src/models/GameMaster/index.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import POGOProtos from "pokemongo-protobuf";
3 |
4 | import {
5 | idToPkmnBundleName
6 | } from "../../utils";
7 |
8 | import CFG from "../../../cfg";
9 | import ENUM from "../../enum";
10 |
11 | import print from "../../print";
12 |
13 | /**
14 | * @class GameMaster
15 | */
16 | export default class GameMaster {
17 |
18 | /**
19 | * @param {GameServer} instance
20 | * @constructor
21 | */
22 | constructor(instance) {
23 |
24 | this.instance = instance;
25 |
26 | this.settings = this.buildSettings();
27 |
28 | this.decode = this.parse();
29 | this.buffer = this.encode();
30 |
31 | this.parseItemTemplates();
32 |
33 | }
34 |
35 | /**
36 | * @return {Buffer}
37 | */
38 | encode() {
39 | return (
40 | POGOProtos.serialize(this.decode, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse")
41 | );
42 | }
43 |
44 | parse() {
45 | let master = null;
46 | try {
47 | let data = fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master");
48 | master = this.instance.parseProtobuf(data, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse");
49 | } catch (e) {
50 | print(e, 31);
51 | }
52 | return (master);
53 | }
54 |
55 | parseItemTemplates() {
56 |
57 | let item = null;
58 | let items = this.decode.item_templates;
59 |
60 | let ii = 0;
61 | let length = items.length;
62 |
63 | for (; ii < length; ++ii) {
64 | item = items[ii];
65 | this.parseKey(item, item.template_id);
66 | };
67 |
68 | }
69 |
70 | /**
71 | * @param {Object} item
72 | * @param {String} key
73 | */
74 | parseKey(item, key) {
75 | if (key in this.settings) {
76 | this.settings[key] = item;
77 | }
78 | }
79 |
80 | /**
81 | * @return {Object}
82 | */
83 | buildSettings() {
84 | let settings = {
85 | "PLAYER_LEVEL_SETTINGS": null
86 | };
87 | return (settings);
88 | }
89 |
90 | getPlayerSettings() {
91 | return (
92 | this.settings["PLAYER_LEVEL_SETTINGS"].player_level
93 | );
94 | }
95 |
96 | /**
97 | * @param {Number} dex
98 | * @return {Object}
99 | */
100 | getPokemonTmplByDex(dex) {
101 |
102 | let id = idToPkmnBundleName(dex).substring(2);
103 | let name = ENUM.getNameById(ENUM.POKEMON_IDS, dex);
104 | let tmplId = `V${id}_POKEMON_${name}`;
105 |
106 | let item = null;
107 | let items = this.decode.item_templates;
108 |
109 | let ii = 0;
110 | let length = items.length;
111 |
112 | for (; ii < length; ++ii) {
113 | item = items[ii];
114 | if (
115 | item.pokemon_settings !== void 0 &&
116 | item.template_id === tmplId
117 | ) {
118 | return (item.pokemon_settings);
119 | }
120 | };
121 |
122 | return (null);
123 |
124 | }
125 |
126 | /**
127 | * @return {Buffer}
128 | */
129 | serialize() {
130 | return (
131 | this.buffer
132 | );
133 | }
134 |
135 | }
--------------------------------------------------------------------------------
/src/models/Player/Avatar/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Avatar
3 | */
4 | export default class Avatar {
5 |
6 | /**
7 | * @param {Player} player
8 | * @constructor
9 | */
10 | constructor(player) {
11 |
12 | this.player = player;
13 |
14 | this._skin = 0;
15 | this._hair = 0;
16 | this._shirt = 0;
17 | this._pants = 0;
18 | this._hat = 0;
19 | this._shoes = 0;
20 | this._eyes = 0;
21 | this._backpack = 0;
22 | this._gender = 0;
23 |
24 | }
25 |
26 | /**
27 | * @param {Number} value
28 | * @param {Number} a
29 | * @param {Number} b
30 | * @return {Boolean}
31 | */
32 | between(value, a, b) {
33 | return (
34 | value >= a && value <= b
35 | );
36 | }
37 |
38 | // skin
39 | get skin() {
40 | return (this._skin);
41 | }
42 | set skin(value) {
43 | if (this.between(value, 0, 3)) {
44 | this._skin = value;
45 | }
46 | }
47 |
48 | // hair
49 | get hair() {
50 | return (this._hair);
51 | }
52 | set hair(value) {
53 | if (this.between(value, 0, 5)) {
54 | this._hair = value;
55 | }
56 | }
57 |
58 | // shirt
59 | get shirt() {
60 | return (this._shirt);
61 | }
62 | set shirt(value) {
63 | if (this.between(value, 0, 9)) {
64 | this._shirt = value;
65 | }
66 | }
67 |
68 | // pants
69 | get pants() {
70 | return (this._pants);
71 | }
72 | set pants(value) {
73 | if (this.between(value, 0, 5)) {
74 | this._pants = value;
75 | }
76 | }
77 |
78 | // hat
79 | get hat() {
80 | return (this._hat);
81 | }
82 | set hat(value) {
83 | if (this.between(value, 0, 4)) {
84 | this._hat = value;
85 | }
86 | }
87 |
88 | // shoes
89 | get shoes() {
90 | return (this._shoes);
91 | }
92 | set shoes(value) {
93 | if (this.between(value, 0, 6)) {
94 | this._shoes = value;
95 | }
96 | }
97 |
98 | // eyes
99 | get eyes() {
100 | return (this._eyes);
101 | }
102 | set eyes(value) {
103 | if (this.between(value, 0, 4)) {
104 | this._eyes = value;
105 | }
106 | }
107 |
108 | // backpack
109 | get backpack() {
110 | return (this._backpack);
111 | }
112 | set backpack(value) {
113 | if (this.between(value, 0, 5)) {
114 | this._backpack = value;
115 | }
116 | }
117 |
118 | // gender
119 | get gender() {
120 | return (this._gender === 0 ? "MALE" : "FEMALE");
121 | }
122 | set gender(value) {
123 | if (this.between(value, 0, 1)) {
124 | this._gender = value;
125 | }
126 | }
127 |
128 | resetOutfit() {
129 | this.skin = 0;
130 | this.hair = 0;
131 | this.shirt = 0;
132 | this.pants = 0;
133 | this.hat = 0;
134 | this.shoes = 0;
135 | this.eyes = 0;
136 | this.backpack = 0;
137 | }
138 |
139 | /**
140 | * @return {Object}
141 | */
142 | serialize() {
143 | return ({
144 | skin: this.skin,
145 | hair: this.hair,
146 | shirt: this.shirt,
147 | pants: this.pants,
148 | hat: this.hat,
149 | shoes: this.shoes,
150 | eyes: this.eyes,
151 | gender: this.gender,
152 | backpack: this.backpack
153 | });
154 | }
155 |
156 | /**
157 | * @return {String}
158 | */
159 | querify() {
160 | return (JSON.stringify({
161 | skin: this.skin,
162 | hair: this.hair,
163 | shirt: this.shirt,
164 | pants: this.pants,
165 | hat: this.hat,
166 | shoes: this.shoes,
167 | eyes: this.eyes,
168 | gender: this.gender,
169 | backpack: this.backpack
170 | }));
171 | }
172 |
173 | /**
174 | * @param {String} str
175 | */
176 | parseJSON(str) {
177 | let obj = JSON.parse(str);
178 | for (let key in obj) {
179 | if (this.hasOwnProperty("_" + key)) {
180 | this[key] = obj[key] << 0;
181 | }
182 | };
183 | }
184 |
185 | }
--------------------------------------------------------------------------------
/src/models/Player/Bag/index.js:
--------------------------------------------------------------------------------
1 | import ENUM from "../../../enum";
2 |
3 | /**
4 | * @class Bag
5 | */
6 | export default class Bag {
7 |
8 | /**
9 | * @param {Player} player
10 | * @constructor
11 | */
12 | constructor(player) {
13 |
14 | this.player = player;
15 |
16 | this.poke_ball = 0;
17 | this.great_ball = 0;
18 | this.ultra_ball = 0;
19 | this.master_ball = 0;
20 |
21 | this.potion = 0;
22 | this.super_potion = 0;
23 | this.hyper_potion = 0;
24 | this.max_potion = 0;
25 |
26 | this.revive = 0;
27 | this.max_revive = 0;
28 |
29 | this.lucky_egg = 0;
30 | this.troy_disk = 0;
31 |
32 | this.incense_ordinary = 0;
33 | this.incense_spicy = 0;
34 | this.incense_cool = 0;
35 | this.incense_floral = 0;
36 |
37 | this.razz_berry = 0;
38 | this.bluk_berry = 0;
39 | this.nanab_berry = 0;
40 | this.wepar_berry = 0;
41 | this.pinap_berry = 0;
42 |
43 | this.incubator_basic = 0;
44 | this.incubator_basic_unlimited = 0;
45 |
46 | this.pokemon_storage_upgrade = 0;
47 | this.storage_upgrade = 0;
48 |
49 | }
50 |
51 | /**
52 | * @param {String} name
53 | * @return {Number}
54 | */
55 | getItemEnumId(name) {
56 | return (
57 | ENUM.getIdByName(ENUM.ITEMS, name)
58 | );
59 | }
60 |
61 | /**
62 | * @param {Number} id
63 | * @return {String}
64 | */
65 | getItemEnumName(id) {
66 | return (
67 | ENUM.getNameById(ENUM.ITEMS, id)
68 | );
69 | }
70 |
71 | /**
72 | * @param {String} name
73 | * @return {String}
74 | */
75 | getItemName(name) {
76 | return (
77 | this.getItemEnumId("ITEM_" + name.toUpperCase())
78 | );
79 | }
80 |
81 | /**
82 | * @param {String} key
83 | * @return {String}
84 | */
85 | getLocalItemKey(key) {
86 | return (
87 | key.replace("ITEM_", "").toLowerCase()
88 | );
89 | }
90 |
91 | /**
92 | * @param {String} key
93 | * @return {Number}
94 | */
95 | getItemAmountByItemKey(key) {
96 | let name = this.getLocalItemKey(key);
97 | if (this.hasOwnProperty(name)) {
98 | return (this[name]);
99 | }
100 | return (-1);
101 | }
102 |
103 | /**
104 | * @param {String} name
105 | * @return {Boolean}
106 | */
107 | isValidItemKey(name) {
108 | return (
109 | this.hasOwnProperty(name) && Number.isInteger(this[name])
110 | );
111 | }
112 |
113 | /**
114 | * @param {String} name
115 | * @param {Number} amount
116 | * @return {Number}
117 | */
118 | updateItem(name, amount) {
119 | let key = this.getLocalItemKey(name);
120 | if (!this.isValidItemKey(key)) return (-1);
121 | let currentAmount = this[key] << 0;
122 | if (amount < 0) {
123 | if (currentAmount + amount < 0) this[key] = 0;
124 | else this[key] += amount;
125 | }
126 | else {
127 | this[key] += amount;
128 | }
129 | return (this[key]);
130 | }
131 |
132 | /**
133 | * @return {Array}
134 | */
135 | serialize() {
136 | let out = [];
137 | for (let key in this) {
138 | if (this.isValidItemKey(key)) {
139 | let itemId = this.getItemName(key);
140 | if (Number.isInteger(itemId) && this[key] > 0) {
141 | out.push({
142 | modified_timestamp_ms: +new Date() - 1e3,
143 | inventory_item_data: {
144 | item: {
145 | item_id: "ITEM_" + key.toUpperCase(),
146 | count: this[key] << 0
147 | }
148 | }
149 | });
150 | }
151 | }
152 | };
153 | return (out);
154 | }
155 |
156 | /**
157 | * @return {String}
158 | */
159 | querify() {
160 | let buffer = {};
161 | for (let key in this) {
162 | if (this.isValidItemKey(key)) {
163 | let itemId = this.getItemName(key);
164 | if (Number.isInteger(itemId)) {
165 | buffer[itemId] = this[key];
166 | }
167 | }
168 | };
169 | return (JSON.stringify(buffer));
170 | }
171 |
172 | /**
173 | * @param {String} str
174 | */
175 | parseJSON(str) {
176 | let obj = JSON.parse(str);
177 | for (let key in obj) {
178 | let name = this.getItemEnumName(key).toLowerCase().replace("item_", "");
179 | if (this.isValidItemKey(name)) {
180 | this[name] = obj[key];
181 | }
182 | };
183 | }
184 |
185 | }
--------------------------------------------------------------------------------
/src/models/Player/CandyBag/index.js:
--------------------------------------------------------------------------------
1 | import { GAME_MASTER } from "../../../shared";
2 |
3 | import ENUM from "../../../enum";
4 |
5 | /**
6 | * @class CandyBag
7 | */
8 | export default class CandyBag {
9 |
10 | /**
11 | * @param {Player} player
12 | * @constructor
13 | */
14 | constructor(player) {
15 |
16 | this.player = player;
17 |
18 | this.candies = {};
19 |
20 | }
21 |
22 | /**
23 | * @param {Number} dex
24 | * @return {Object}
25 | */
26 | getPkmnTemplate(dex) {
27 | let tmpl = GAME_MASTER.getPokemonTmplByDex(dex);
28 | return (tmpl);
29 | }
30 |
31 | /**
32 | * @param {Number} dex
33 | * @return {String}
34 | */
35 | getPkmnFamily(dex) {
36 | return (
37 | this.getPkmnTemplate(dex).family_id
38 | );
39 | }
40 |
41 | /**
42 | * @param {Number} dex
43 | * @return {Object}
44 | */
45 | createCandy(dex) {
46 | let id = ENUM.getIdByName(ENUM.POKEMON_FAMILY, this.getPkmnFamily(dex << 0));
47 | let candy = {
48 | amount: 0
49 | };
50 | this.candies[id] = candy;
51 | return (candy);
52 | }
53 |
54 | /**
55 | * @param {Number} dex
56 | * @return {Object}
57 | */
58 | getCandyByDexNumber(dex) {
59 | let id = ENUM.getIdByName(ENUM.POKEMON_FAMILY, this.getPkmnFamily(dex << 0));
60 | if (this.candies[id] !== void 0) {
61 | return (this.candies[id]);
62 | }
63 | else {
64 | return (this.createCandy(id) || null);
65 | }
66 | }
67 |
68 | /**
69 | * @param {Number} dex
70 | * @return {Number}
71 | */
72 | getCandy(dex) {
73 | return (
74 | this.getCandyByDexNumber(dex)
75 | );
76 | }
77 |
78 | /**
79 | * @param {Number} dex
80 | * @param {Number} amount
81 | */
82 | addCandy(dex, amount) {
83 | let candy = this.getCandyByDexNumber(dex);
84 | candy.amount += parseInt(amount);
85 | }
86 |
87 | /**
88 | * @param {Number} dex
89 | * @param {Number} amount
90 | */
91 | removeCandy(dex, amount) {
92 | let candy = this.getCandyByDexNumber(dex);
93 | candy.amount -= parseInt(amount);
94 | if (candy.amount < 0) candy.amount = 0;
95 | }
96 |
97 | /**
98 | * @return {Array}
99 | */
100 | serialize() {
101 | let out = [];
102 | for (let key in this.candies) {
103 | let candy = this.candies[key];
104 | if (!(candy.amount > 0)) continue;
105 | out.push({
106 | modified_timestamp_ms: +new Date() - 1e3,
107 | inventory_item_data: {
108 | candy: {
109 | family_id: this.getPkmnFamily(key << 0),
110 | candy: candy.amount
111 | }
112 | }
113 | });
114 | };
115 | return (out);
116 | }
117 |
118 | /**
119 | * @return {String}
120 | */
121 | querify() {
122 | let buffer = {};
123 | for (let key in this.candies) {
124 | let candy = this.candies[key].amount;
125 | buffer[key] = candy;
126 | };
127 | return (JSON.stringify(buffer));
128 | }
129 |
130 | /**
131 | * @param {String} str
132 | */
133 | parseJSON(str) {
134 | let candies = JSON.parse(str);
135 | for (let candy in candies) {
136 | this.createCandy(candy).amount = parseInt(candies[candy]);
137 | };
138 | }
139 |
140 | }
--------------------------------------------------------------------------------
/src/models/Player/Contact/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Contact
3 | */
4 | export default class Contact {
5 |
6 | /**
7 | * @param {Player} player
8 | * @constructor
9 | */
10 | constructor(player) {
11 |
12 | this.player = player;
13 |
14 | this.sendMarketingEmails = false;
15 | this.sendPushNotifications = false;
16 |
17 | }
18 |
19 | /**
20 | * @return {Object}
21 | */
22 | serialize() {
23 | return ({
24 | send_marketing_emails: this.sendMarketingEmails,
25 | send_push_notifications: this.sendPushNotifications
26 | });
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/models/Player/Currency/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Currency
3 | */
4 | export default class Currency {
5 |
6 | /**
7 | * @param {Player} player
8 | * @constructor
9 | */
10 | constructor(player) {
11 |
12 | this.player = player;
13 |
14 | }
15 |
16 | /**
17 | * @return {Array}
18 | */
19 | serialize() {
20 | let out = [];
21 | out.push({
22 | name: "POKECOIN",
23 | amount: this.player.info.pokecoin
24 | });
25 | out.push({
26 | name: "STARDUST",
27 | amount: this.player.info.stardust
28 | });
29 | return (out);
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/src/models/Player/Info/index.js:
--------------------------------------------------------------------------------
1 | import { GAME_MASTER } from "../../../shared";
2 |
3 | import print from "../../../print";
4 |
5 | import ENUM from "../../../enum";
6 |
7 | /**
8 | * @class Info
9 | */
10 | export default class Info {
11 |
12 | /**
13 | * @param {Player} player
14 | * @constructor
15 | */
16 | constructor(player) {
17 |
18 | this.player = player;
19 |
20 | this.stardust = 0;
21 | this.pokecoins = 0;
22 |
23 | this._exp = 0;
24 | this._team = 0;
25 | this._level = 0;
26 |
27 | this.prevLvlExp = 0;
28 | this.nextLvlExp = 0;
29 |
30 | this.levelReward = false;
31 |
32 | this.kmWalked = 0;
33 | this.pkmnEncountered = 1;
34 | this.uniquePokedexEntries = 1;
35 | this.pkmnCaptured = 1;
36 | this.pokeStopVisits = 2;
37 | this.pokeballsThrown = 3;
38 | this.eggsHatched = 0;
39 | this.bigMagikarpCaught = 0;
40 | this.pkmnDeployed = 0;
41 |
42 | this.maxPkmnStorage = 250;
43 | this.maxItemStorage = 350;
44 |
45 | }
46 |
47 | get exp() {
48 | return (this._exp);
49 | }
50 | set exp(value) {
51 | this._exp = value;
52 | this.updatePrevNextExp();
53 | }
54 |
55 | get level() {
56 | return (this._level);
57 | }
58 | set level(value) {
59 | this._level = value;
60 | this.updatePrevNextExp();
61 | }
62 |
63 | get team() {
64 | return (ENUM.getNameById(ENUM.TEAM, this._team));
65 | }
66 | set team(value) {
67 | this._team = value << 0;
68 | }
69 |
70 | updatePrevNextExp() {
71 | this.prevLvlExp = this.getLevelExp(this.level);
72 | this.nextLvlExp = this.getLevelExp(this.level + 1);
73 | }
74 |
75 | upgradeLevel() {
76 |
77 | }
78 |
79 | getLevelSettings() {
80 | return (
81 | GAME_MASTER.settings.PLAYER_LEVEL_SETTINGS.player_level
82 | );
83 | }
84 |
85 | getMaximumLevel() {
86 | return (
87 | this.getLevelSettings().required_experience.length
88 | );
89 | }
90 |
91 | getCurrentLevel() {
92 | let levels = this.getLevelSettings().required_experience;
93 | for (let key in levels) {
94 | if (levels[key] << 0 === this.nextLvlExp) {
95 | return (key << 0);
96 | }
97 | };
98 | return (1);
99 | }
100 |
101 | getLevelExp(lvl) {
102 | return (
103 | this.getLevelSettings().required_experience[lvl - 1]
104 | );
105 | }
106 |
107 | /**
108 | * @param {Number} exp
109 | */
110 | upgradeExp(exp) {
111 | if (!this.maxLevelReached()) {
112 | let currentLevelExp = this.getLevelExp(this.level);
113 | let nextLevelExp = this.getLevelExp(this.level + 1);
114 | let leftExp = nextLevelExp - this.exp;
115 | this.levelReward = false;
116 | if (this.exp + exp >= nextLevelExp) {
117 | this.level += 1;
118 | this.prevLvlExp = nextLevelExp;
119 | this.nextLvlExp = this.getLevelExp(this.level + 1);
120 | this.exp += leftExp + 1;
121 | this.levelReward = true;
122 | this.upgradeExp(exp - leftExp);
123 | }
124 | else {
125 | this.exp += exp;
126 | }
127 | }
128 | }
129 |
130 | /**
131 | * @return {Boolean}
132 | */
133 | maxLevelReached() {
134 | return (
135 | this.level + 1 >= this.getMaximumLevel()
136 | );
137 | }
138 |
139 | /**
140 | * @return {Object}
141 | */
142 | serialize() {
143 | return ({
144 | modified_timestamp_ms: +new Date(),
145 | inventory_item_data: {
146 | player_stats: {
147 | level: this.level,
148 | experience: this.exp,
149 | prev_level_xp: this.prevLvlExp,
150 | next_level_xp: this.nextLvlExp,
151 | km_walked: this.kmWalked,
152 | pokemons_encountered: this.pkmnEncountered,
153 | unique_pokedex_entries: this.uniquePokedexEntries,
154 | pokemons_captured: this.pkmnCaptured,
155 | poke_stop_visits: this.pokeStopVisits,
156 | pokeballs_thrown: this.pokeballsThrown,
157 | eggs_hatched: this.eggsHatched,
158 | big_magikarp_caught: this.bigMagikarpCaught,
159 | pokemon_deployed: this.pkmnDeployed
160 | }
161 | }
162 | });
163 | }
164 |
165 | }
--------------------------------------------------------------------------------
/src/models/Player/Party/index.js:
--------------------------------------------------------------------------------
1 | import Pokemon from "../../Pokemon";
2 | import WildPokemon from "../../Pokemon/WildPokemon";
3 |
4 | import print from "../../../print";
5 |
6 | import CFG from "../../../../cfg";
7 |
8 | /**
9 | * @class Party
10 | */
11 | export default class Party {
12 |
13 | /**
14 | * @param {Player} player
15 | * @constructor
16 | */
17 | constructor(player) {
18 |
19 | this.player = player;
20 |
21 | this.party = [];
22 |
23 | }
24 |
25 | syncWithDatabase() {
26 | let query = `SELECT * FROM ${CFG.MYSQL_OWNED_PKMN_TABLE} WHERE owner_id=?`;
27 | return new Promise((resolve) => {
28 | this.player.world.db.query(query, [this.player.uid], (e, rows) => {
29 | if (e) return print(e, 31);
30 | rows.map((row) => {
31 | row.isOwned = true;
32 | this.addPkmn(row);
33 | });
34 | resolve();
35 | });
36 | });
37 | }
38 |
39 | /**
40 | * @param {Object} obj
41 | * @return {Pokemon}
42 | */
43 | addPkmn(obj) {
44 | obj.owner = this.player;
45 | let pkmn = new Pokemon(obj);
46 | this.party.push(pkmn);
47 | return (pkmn);
48 | }
49 |
50 | /**
51 | * @param {Number} id
52 | * @return {Number}
53 | */
54 | getPkmnIndexById(id) {
55 | id = parseInt(id);
56 | for (let ii = 0; ii < this.party.length; ++ii) {
57 | if (this.party[ii].uid === id) return (ii);
58 | };
59 | return (-1);
60 | }
61 |
62 | /**
63 | * @param {Number} id
64 | * @return {Pokemon}
65 | */
66 | getPkmnById(id) {
67 | let index = this.getPkmnIndexById(id);
68 | return (this.party[index]);
69 | }
70 |
71 | /**
72 | * @param {Number} id
73 | */
74 | deletePkmn(id) {
75 | let index = this.getPkmnIndexById(id);
76 | let pkmn = this.party[index];
77 | if (pkmn) this.party.splice(index, 1);
78 | }
79 |
80 | /**
81 | * @return {Number}
82 | */
83 | getUniquePkmnCount() {
84 | let ii = 0;
85 | let dex = 0;
86 | let amount = 0;
87 | let length = this.party.length;
88 | let array = [];
89 | for (; ii < length; ++ii) {
90 | dex = this.party[ii].dexNumber;
91 | if (array.indexOf(dex) === -1) {
92 | array.push(dex);
93 | amount++;
94 | }
95 | };
96 | return (amount);
97 | }
98 |
99 | /**
100 | * @return {Array}
101 | */
102 | serialize() {
103 | let out = [];
104 | let ii = 0;
105 | let length = this.party.length;
106 | for (; ii < length; ++ii) {
107 | out.push({
108 | "inventory_item_data": {
109 | "pokemon_data": this.party[ii].serialize()
110 | }
111 | });
112 | };
113 | return (out);
114 | }
115 |
116 | }
--------------------------------------------------------------------------------
/src/models/Player/PokeDex/index.js:
--------------------------------------------------------------------------------
1 | import { GAME_MASTER } from "../../../shared";
2 |
3 | import ENUM from "../../../enum";
4 |
5 | /**
6 | * @class PokeDex
7 | */
8 | export default class PokeDex {
9 |
10 | /**
11 | * @param {Player} player
12 | * @constructor
13 | */
14 | constructor(player) {
15 |
16 | this.player = player;
17 |
18 | this.pkmns = {};
19 |
20 | }
21 |
22 | /**
23 | * @param {Number} dex
24 | * @return {Boolean}
25 | */
26 | entryExists(dex) {
27 | return (
28 | this.pkmns.hasOwnProperty(dex)
29 | );
30 | }
31 |
32 | /**
33 | * @param {Number} dex
34 | * @param {Number} capture
35 | * @param {Number} encounter
36 | */
37 | addEntry(dex, capture, encounter) {
38 | if (this.entryExists(dex)) {
39 | this.pkmns[dex].captured += capture << 0;
40 | this.pkmns[dex].encountered += encounter << 0;
41 | } else {
42 | this.pkmns[dex] = {
43 | captured: 0,
44 | encountered: 0
45 | };
46 | this.addEntry(dex, capture, encounter);
47 | }
48 | }
49 |
50 | /**
51 | * @param {Number} dex
52 | */
53 | removeEntry(dex) {
54 | if (this.entryExists(dex)) {
55 | delete this.pkmns[dex];
56 | }
57 | }
58 |
59 | /**
60 | * @return {Array}
61 | */
62 | serialize() {
63 | let out = [];
64 | for (let key in this.pkmns) {
65 | let pkmn = this.pkmns[key];
66 | out.push({
67 | modified_timestamp_ms: +new Date() - 1e3,
68 | inventory_item_data: {
69 | pokedex_entry: {
70 | pokemon_id: key,
71 | times_captured: this.pkmns[key].captured,
72 | times_encountered: this.pkmns[key].encountered
73 | }
74 | }
75 | });
76 | };
77 | return (out);
78 | }
79 |
80 | /**
81 | * @param {String} str
82 | */
83 | parseJSON(str) {
84 | let pkmns = JSON.parse(str);
85 | for (let pkmn in pkmns) {
86 | this.addEntry(pkmn);
87 | };
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/src/models/Player/Tutorial/index.js:
--------------------------------------------------------------------------------
1 | import ENUM from "../../../enum";
2 |
3 | /**
4 | * @class Tutorial
5 | */
6 | export default class Tutorial {
7 |
8 | /**
9 | * @param {Player} player
10 | * @constructor
11 | */
12 | constructor(player) {
13 |
14 | this.player = player;
15 |
16 | this.states = [];
17 |
18 | }
19 |
20 | /**
21 | * @param {String} name
22 | * @return {String}
23 | */
24 | getItemName(name) {
25 | return (
26 | ENUM.getNameById(ENUM.TUTORIAL, name)
27 | );
28 | }
29 |
30 | skipTutorial() {
31 | this.states = [
32 | "LEGAL_SCREEN",
33 | "AVATAR_SELECTION",
34 | "POKEMON_CAPTURE",
35 | "NAME_SELECTION",
36 | "FIRST_TIME_EXPERIENCE_COMPLETE"
37 | ];
38 | }
39 |
40 | /**
41 | * @return {Array}
42 | */
43 | serialize() {
44 | return (
45 | this.states
46 | );
47 | }
48 |
49 | /**
50 | * @return {String}
51 | */
52 | querify() {
53 | let buffer = {};
54 | for (let key in this.states) {
55 | let itemId = this.getItemName(key);
56 | buffer[itemId] = this[key];
57 | };
58 | return (JSON.stringify(buffer));
59 | }
60 |
61 | /**
62 | * @param {String} str
63 | */
64 | parseJSON(str) {
65 | this.states = [];
66 | let obj = JSON.parse(str);
67 | for (let key in obj) {
68 | let name = this.getItemName(key);
69 | if (obj[key] === 1) {
70 | this.states.push(name.toUpperCase());
71 | }
72 | };
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/src/models/Player/index.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import Bag from "./Bag";
4 | import Info from "./Info";
5 | import Party from "./Party";
6 | import Avatar from "./Avatar";
7 | import Pokedex from "./PokeDex";
8 | import Contact from "./Contact";
9 | import CandyBag from "./CandyBag";
10 | import Tutorial from "./Tutorial";
11 | import Currency from "./Currency";
12 |
13 | import MapObject from "../World/MapObject";
14 |
15 | import print from "../../print";
16 | import CFG from "../../../cfg";
17 |
18 | import * as _packets from "./packets";
19 |
20 | import {
21 | inherit,
22 | parseSignature
23 | } from "../../utils";
24 |
25 | import ENUM from "../../enum";
26 |
27 | import { GAME_MASTER } from "../../shared";
28 |
29 | /**
30 | * @class Player
31 | */
32 | export default class Player extends MapObject {
33 |
34 | /**
35 | * @param {Object} obj
36 | * @constructor
37 | */
38 | constructor(obj) {
39 |
40 | super(null);
41 |
42 | this.world = obj.world;
43 |
44 | this._email = null;
45 |
46 | this.username = "unknown";
47 |
48 | this.email_verified = false;
49 |
50 | this.platform = null;
51 |
52 | this.isPTCAccount = false;
53 | this.isGoogleAccount = false;
54 |
55 | this.isIOS = false;
56 | this.isAndroid = false;
57 |
58 | this.hasSignature = false;
59 |
60 | this.authenticated = false;
61 |
62 | this.request = null;
63 | this.response = null;
64 |
65 | this.remoteAddress = null;
66 |
67 | this.currentEncounter = null;
68 |
69 | this.bag = new Bag(this);
70 |
71 | this.info = new Info(this);
72 | this.party = new Party(this);
73 | this.avatar = new Avatar(this);
74 | this.pokeDex = new Pokedex(this);
75 | this.contact = new Contact(this);
76 | this.candyBag = new CandyBag(this);
77 | this.tutorial = new Tutorial(this);
78 | this.currency = new Currency(this);
79 |
80 | this.refreshSocket(obj.request, obj.response);
81 |
82 | }
83 |
84 | get email() {
85 | return (this._email);
86 | }
87 | set email(value) {
88 | this._email = value;
89 | if (this.username === "unknown") this.username = value.replace("@gmail.com", "");
90 | }
91 |
92 | /**
93 | * @param {Buffer} buffer
94 | */
95 | sendResponse(buffer) {
96 | this.response.end(buffer);
97 | }
98 |
99 | /**
100 | * @param {Request} req
101 | * @param {String} type
102 | * @return {Boolean}
103 | */
104 | requestContains(req, type) {
105 | let requests = req.requests;
106 | for (let request of requests) {
107 | if (request.request_type === type) return (true);
108 | };
109 | return (false);
110 | }
111 |
112 | /**
113 | * @param {Request} req
114 | * @param {Response} res
115 | */
116 | refreshSocket(req, res) {
117 | this.request = POGOProtos.parseWithUnknown(req.body, "POGOProtos.Networking.Envelopes.RequestEnvelope");
118 | this.response = res;
119 | // Try to update players position on each req
120 | this.refreshPosition();
121 | }
122 |
123 | refreshPosition() {
124 | let req = this.request;
125 | if (
126 | req.latitude !== void 0 &&
127 | req.longitude !== void 0
128 | ) {
129 | this.latitude = req.latitude;
130 | this.longitude = req.longitude;
131 | }
132 | if (this.requestContains(req, "GET_MAP_OBJECTS")) {
133 | this.world.triggerSpawnAt(this.latitude, this.longitude);
134 | }
135 | }
136 |
137 | getDevicePlatform() {
138 | let request = this.request;
139 | if (request.unknown6 && request.unknown6[0]) {
140 | let sig = parseSignature(request);
141 | if (sig.device_info !== void 0) {
142 | this.hasSignature = true;
143 | this.isIOS = sig.device_info.device_brand === "Apple";
144 | this.isAndroid = !this.isIOS;
145 | this.platform = this.isIOS ? "ios" : "android";
146 | print(`${this.email} is playing with an ${this.isIOS ? "Apple" : "Android"} device!`, 36);
147 | }
148 | }
149 | }
150 |
151 | /**
152 | * @param {String} type
153 | * @param {Object} msg
154 | */
155 | getPacket(type, msg) {
156 | return new Promise((resolve) => {
157 | switch (type) {
158 | case "SET_FAVORITE_POKEMON":
159 | resolve(this.SetFavoritePokemon(msg));
160 | break;
161 | case "LEVEL_UP_REWARDS":
162 | resolve(this.LevelUpRewards(msg));
163 | break;
164 | case "RELEASE_POKEMON":
165 | this.ReleasePokemon(msg).then((result) => {
166 | resolve(result);
167 | });
168 | break;
169 | case "UPGRADE_POKEMON":
170 | resolve(this.UpgradePokemon(msg));
171 | break;
172 | case "GET_PLAYER_PROFILE":
173 | resolve(this.GetPlayerProfile(msg));
174 | break;
175 | case "SET_AVATAR":
176 | resolve(this.SetAvatar(msg));
177 | break;
178 | case "GET_PLAYER":
179 | resolve(this.GetPlayer(msg));
180 | break;
181 | case "GET_INVENTORY":
182 | resolve(this.GetInventory(msg));
183 | break;
184 | case "GET_ASSET_DIGEST":
185 | resolve(this.GetAssetDigest(msg));
186 | break;
187 | case "NICKNAME_POKEMON":
188 | resolve(this.NicknamePokemon(msg));
189 | break;
190 | case "GET_HATCHED_EGGS":
191 | resolve(this.GetHatchedEggs(msg));
192 | break;
193 | case "CHECK_AWARDED_BADGES":
194 | resolve(this.CheckAwardedBadges(msg));
195 | break;
196 | case "RECYCLE_INVENTORY_ITEM":
197 | resolve(this.RecycleInventoryItem(msg));
198 | break;
199 | case "CLAIM_CODENAME":
200 | resolve(this.ClaimCodename(msg));
201 | break;
202 | };
203 | });
204 | }
205 |
206 | /**
207 | * @return {Object}
208 | */
209 | getPlayerData() {
210 | return ({
211 | creation_timestamp_ms: +new Date(),
212 | username: this.username,
213 | team: this.info.team,
214 | tutorial_state: this.tutorial.serialize(),
215 | avatar: this.avatar.serialize(),
216 | max_pokemon_storage: 250,
217 | max_item_storage: 350,
218 | daily_bonus: {},
219 | equipped_badge: {},
220 | contact_settings: this.contact.serialize(),
221 | currencies: this.currency.serialize(),
222 | remaining_codename_claims: 10
223 | });
224 | }
225 |
226 | inheritByObject(obj) {
227 | for (let key in obj) {
228 | // ignore
229 | if (!(key !== "email")) continue;
230 | if (key === "id") {
231 | this.uid = obj[key];
232 | }
233 | else if (key === "candies") {
234 | this.candyBag.parseJSON(obj[key]);
235 | }
236 | else if (key === "items") {
237 | this.bag.parseJSON(obj[key]);
238 | }
239 | else if (key === "avatar") {
240 | this.avatar.parseJSON(obj[key]);
241 | }
242 | else if (key === "tutorial") {
243 | this.tutorial.parseJSON(obj[key]);
244 | }
245 | else if (key === "pokecoins") {
246 | this.info.pokecoins = obj[key] << 0;
247 | }
248 | else if (key === "stardust") {
249 | this.info.stardust = obj[key] << 0;
250 | }
251 | else if (key === "level") {
252 | this.info.level = obj[key] << 0;
253 | }
254 | else if (key === "exp") {
255 | this.info.exp = obj[key] << 0;
256 | }
257 | else if (key === "team") {
258 | this.info.team = obj[key] << 0;
259 | }
260 | else {
261 | if (this.hasOwnProperty(key)) {
262 | this[key] = obj[key];
263 | }
264 | }
265 | };
266 | }
267 |
268 | syncWithDatabase() {
269 | return new Promise((resolve) => {
270 | this.loadPlayerDatabase().then((row) => {
271 | this.party.syncWithDatabase().then(() => {
272 | resolve();
273 | });
274 | });
275 | });
276 | }
277 |
278 | loadPlayerDatabase() {
279 | let query = `SELECT * from ${CFG.MYSQL_USERS_TABLE} WHERE email=? LIMIT 1`;
280 | return new Promise((resolve) => {
281 | this.world.db.query(query, [this.email], (e, rows) => {
282 | if (e) return print(e, 31);
283 | if (rows.length >= 1) {
284 | this.inheritByObject(rows[0]);
285 | resolve();
286 | }
287 | else print(`Failed to sync player ${this.username} with database!`, 31);
288 | });
289 | });
290 | }
291 |
292 | /**
293 | * @param {Fort} fort
294 | */
295 | consumeFortRewards(fort) {
296 | let rewards = fort.rewards;
297 | for (let key in rewards) {
298 | let name = ENUM.getNameById(ENUM.ITEMS, key << 0).replace("ITEM_", "").toLowerCase();
299 | if (this.bag.hasOwnProperty(name)) {
300 | this.bag[name] += rewards[key] << 0;
301 | }
302 | };
303 | }
304 |
305 | /**
306 | * @param {WildPokemon} pkmn
307 | * @param {String} ball
308 | */
309 | catchPkmn(pkmn, ball) {
310 | this.info.exp += 100;
311 | this.info.stardust += 100;
312 | this.info.pkmnCaptured += 1;
313 | this.currentEncounter = null;
314 | pkmn.caughtBy(this);
315 | pkmn.pokeball = ball;
316 | return new Promise((resolve) => {
317 | pkmn.owner = this;
318 | pkmn.insertIntoDatabase().then((insertId) => {
319 | let cp = pkmn.getSeenCp(this);
320 | pkmn.isOwned = false;
321 | pkmn = this.party.addPkmn(pkmn);
322 | pkmn.isWild = false;
323 | pkmn.isOwned = true;
324 | pkmn.cp = cp;
325 | pkmn.uid = insertId;
326 | pkmn.addCandies(3);
327 | print(`${this.username} caught a wild ${pkmn.getPkmnName()}!`);
328 | resolve({
329 | status: "CATCH_SUCCESS",
330 | captured_pokemon_id: pkmn.uid,
331 | capture_award: {
332 | activity_type: ["ACTIVITY_CATCH_POKEMON"],
333 | xp: [100],
334 | candy: [3],
335 | stardust: [100]
336 | }
337 | });
338 | });
339 | });
340 | }
341 |
342 | /**
343 | * @param {WildPokemon} pkmn
344 | */
345 | releasePkmn(pkmn) {
346 | pkmn.addCandies(3);
347 | this.party.deletePkmn(pkmn.uid);
348 | return new Promise((resolve) => {
349 | pkmn.deleteFromDatabase().then(() => {
350 | resolve();
351 | });
352 | });
353 | }
354 |
355 | /**
356 | * @param {string} codename
357 | */
358 | setUsername(codename) {
359 | this.username = codename;
360 | }
361 |
362 | }
363 |
364 | inherit(Player, _packets);
365 |
--------------------------------------------------------------------------------
/src/models/Player/packets/CheckAwardedBadges.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function CheckAwardedBadges(msg) {
8 |
9 | let buffer = {
10 | success: true,
11 | awarded_badges: [],
12 | awarded_badge_levels: []
13 | };
14 |
15 | return (
16 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CheckAwardedBadgesResponse")
17 | );
18 |
19 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/ClaimCodename.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function ClaimCodename(msg) {
8 |
9 | let buffer = null;
10 | let schema = "POGOProtos.Networking.Responses.ClaimCodenameResponse";
11 |
12 | let codename = msg.codename;
13 | if(codename) this.setUsername(codename);
14 |
15 | buffer = {
16 | codename: codename,
17 | "user_message": "Testing message",
18 | "is_assignable": true,
19 | status: null,
20 | updated_player: null
21 | };
22 |
23 | buffer.status = codename ? "SUCCESS" : "CODENAME_NOT_AVAILABLE";
24 | buffer.updated_player = this.getPlayerData();
25 |
26 |
27 | return (POGOProtos.serialize(buffer, schema));
28 |
29 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/GetAssetDigest.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import * as shared from "../../../shared";
4 |
5 | /**
6 | * @param {Object} msg
7 | * @return {Buffer}
8 | */
9 | export default function GetAssetDigest(msg) {
10 | return (
11 | shared.GAME_ASSETS[this.platform].buffer
12 | );
13 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/GetAuthTicket.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | export default function GetAuthTicket(id) {
4 |
5 | let buffer = ({
6 | status_code: 53,
7 | request_id: id,
8 | api_url: "pgorelease.nianticlabs.com/custom",
9 | auth_ticket: {
10 | start: new Buffer(""),
11 | expire_timestamp_ms: ((new Date).getTime() + (1000 * 60 * 30)),
12 | end: new Buffer("")
13 | }
14 | });
15 |
16 | return (
17 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Envelopes.ResponseEnvelope")
18 | );
19 |
20 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/GetHatchedEggs.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function GetHatchedEggs(msg) {
8 |
9 | let buffer = {
10 | success: true,
11 | pokemon_id: [],
12 | experience_awarded: [],
13 | candy_awarded: [],
14 | stardust_awarded: []
15 | };
16 |
17 | return (
18 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetHatchedEggsResponse")
19 | );
20 |
21 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/GetInventory.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function GetInventory(msg) {
8 |
9 | let items = this.bag.serialize();
10 | let stats = this.info.serialize();
11 | let party = this.party.serialize();
12 | let candies = this.candyBag.serialize();
13 | let currencies = this.currency.serialize();
14 | //let pokedex = this.pokedex.serialize();
15 |
16 | items.push(stats);
17 | items.push(candies);
18 | items.push(currencies);
19 |
20 | candies.map((candy) => {
21 | items.push(candy);
22 | });
23 |
24 | party.map((pkmn) => {
25 | items.push(pkmn);
26 | });
27 |
28 | let buffer = {
29 | success: true,
30 | inventory_delta: {
31 | new_timestamp_ms: +new Date(),
32 | inventory_items: items
33 | }
34 | };
35 |
36 | return (
37 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetInventoryResponse")
38 | );
39 |
40 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/GetPlayer.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import ENUM from "../../../enum";
4 |
5 | /**
6 | * @param {Object} msg
7 | * @return {Buffer}
8 | */
9 | export default function GetPlayer(msg) {
10 |
11 | let buffer = {
12 | success: true,
13 | player_data: this.getPlayerData()
14 | };
15 |
16 | return (
17 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerResponse")
18 | );
19 |
20 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/GetPlayerProfile.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | export default function GetPlayerProfile() {
4 |
5 | let buffer = ({
6 | result: "SUCCESS",
7 | start_time: +new Date(),
8 | badges: [
9 | {
10 | badge_type: "BADGE_TRAVEL_KM",
11 | end_value: 2674,
12 | current_value: 1337
13 | }
14 | ]
15 | });
16 |
17 | return (
18 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerProfileResponse")
19 | );
20 |
21 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/LevelUpRewards.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function LevelUpRewards(msg) {
8 |
9 | let buffer = {
10 | result: "SUCCESS",
11 | items_awarded: [
12 | {
13 | item_id: "ITEM_POKE_BALL",
14 | item_count: 2
15 | },
16 | {
17 | item_id: "ITEM_TROY_DISK",
18 | item_count: 2
19 | }
20 | ],
21 | items_unlocked: []
22 | };
23 |
24 | return (
25 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.LevelUpRewardsResponse")
26 | );
27 |
28 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/NicknamePokemon.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function NicknamePokemon(msg) {
8 |
9 | let buffer = null;
10 | let schema = "POGOProtos.Networking.Responses.NicknamePokemonResponse";
11 |
12 | let pkmn = this.party.getPkmnById(msg.pokemon_id);
13 |
14 | buffer = { result: null };
15 |
16 | if (pkmn) pkmn.setNickname(String(msg.nickname));
17 | buffer.result = pkmn ? "SUCCESS" : "ERROR_INVALID_NICKNAME";
18 |
19 | return (POGOProtos.serialize(buffer, schema));
20 |
21 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/RecycleInventoryItem.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function RecycleInventoryItem(msg) {
8 |
9 | let buffer = null;
10 |
11 | let success = this.bag.updateItem(msg.item_id, -(msg.count << 0));
12 |
13 | buffer = {
14 | result: success !== -1 ? "SUCCESS" : "ERROR_NOT_ENOUGH_COPIES",
15 | new_count: success,
16 | };
17 |
18 | return (
19 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.RecycleInventoryItemResponse")
20 | );
21 |
22 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/ReleasePokemon.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function ReleasePokemon(msg) {
8 |
9 | let buffer = null;
10 | let schema = "POGOProtos.Networking.Responses.ReleasePokemonResponse";
11 |
12 | let pkmn = this.party.getPkmnById(msg.pokemon_id);
13 |
14 | return new Promise((resolve) => {
15 | if (pkmn) {
16 | this.releasePkmn(pkmn).then((result) => {
17 | buffer = {
18 | result: "SUCCESS",
19 | candy_awarded: 3
20 | };
21 | resolve(POGOProtos.serialize(buffer, schema));
22 | });
23 | } else {
24 | buffer = {
25 | result: "FAILED"
26 | };
27 | resolve(POGOProtos.serialize(buffer, schema));
28 | }
29 | });
30 |
31 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/SetAvatar.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function SetAvatar(msg) {
8 |
9 | if (!msg.player_avatar.hasOwnProperty("gender")) {
10 | msg.player_avatar.gender = "MALE";
11 | }
12 |
13 | let gender = msg.player_avatar.gender;
14 | let genderChange = this.avatar.gender !== gender;
15 |
16 | msg.player_avatar.gender = gender === "MALE" ? 0 : 1;
17 |
18 | if (genderChange) {
19 | this.avatar.resetOutfit();
20 | }
21 |
22 | for (let key in msg.player_avatar) {
23 | this.avatar[key] = msg.player_avatar[key];
24 | };
25 |
26 | let buffer = ({
27 | status: "SUCCESS",
28 | player_data: this.getPlayerData()
29 | });
30 |
31 | return (
32 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.SetAvatarResponse")
33 | );
34 |
35 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/SetFavoritePokemon.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function SetFavoritePokemon(msg) {
8 |
9 | let buffer = null;
10 | let pkmn = this.party.getPkmnById(msg.pokemon_id);
11 |
12 | if (pkmn) {
13 | pkmn.setFavorite(msg.is_favorite);
14 | buffer = { result: "SUCCESS" };
15 | }
16 | else {
17 | buffer = { result: "ERROR_POKEMON_NOT_FOUND" };
18 | }
19 |
20 | return (
21 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.SetFavoritePokemonResponse")
22 | );
23 |
24 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/UpgradePokemon.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | * @return {Buffer}
6 | */
7 | export default function UpgradePokemon(msg) {
8 |
9 | let buffer = null;
10 | let pkmn = this.party.getPkmnById(msg.pokemon_id);
11 |
12 | if (pkmn) {
13 | if (pkmn.powerUp()) {
14 | buffer = { result: "SUCCESS" };
15 | } else {
16 | buffer = { result: "ERROR_INSUFFICIENT_RESOURCES" };
17 | }
18 | buffer.upgraded_pokemon = pkmn.serialize();
19 | }
20 | else {
21 | buffer = { result: "ERROR_POKEMON_NOT_FOUND" };
22 | }
23 |
24 | return (
25 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.UpgradePokemonResponse")
26 | );
27 |
28 | }
--------------------------------------------------------------------------------
/src/models/Player/packets/index.js:
--------------------------------------------------------------------------------
1 | export SetAvatar from "./SetAvatar";
2 | export GetPlayer from "./GetPlayer";
3 | export GetInventory from "./GetInventory";
4 | export GetAuthTicket from "./GetAuthTicket";
5 | export ReleasePokemon from "./ReleasePokemon";
6 | export UpgradePokemon from "./UpgradePokemon";
7 | export LevelUpRewards from "./LevelUpRewards";
8 | export GetHatchedEggs from "./GetHatchedEggs";
9 | export GetAssetDigest from "./GetAssetDigest";
10 | export NicknamePokemon from "./NicknamePokemon";
11 | export GetPlayerProfile from "./GetPlayerProfile";
12 | export SetFavoritePokemon from "./SetFavoritePokemon";
13 | export CheckAwardedBadges from "./CheckAwardedBadges";
14 | export RecycleInventoryItem from "./RecycleInventoryItem";
15 | export ClaimCodename from "./ClaimCodename";
--------------------------------------------------------------------------------
/src/models/Pokemon/WildPokemon/index.js:
--------------------------------------------------------------------------------
1 | import Pokemon from "../index";
2 |
3 | import {
4 | getUniqueHash,
5 | getHashCodeFrom
6 | } from "../../../utils";
7 |
8 | /**
9 | * @class WildPokemon
10 | */
11 | export default class WildPokemon extends Pokemon {
12 |
13 | /**
14 | * @param {Object} obj
15 | * @constructor
16 | */
17 | constructor(obj) {
18 |
19 | super(obj);
20 |
21 | this.uid = getUniqueHash();
22 |
23 | this.encounterId = this.getEncounterId();
24 |
25 | this.despawnIn = -1;
26 | this.isDespawned = false;
27 |
28 | this.minExpire = obj.minExpire;
29 | this.maxExpire = obj.maxExpire;
30 |
31 | this.creation = +new Date();
32 |
33 | this.expiration = (Math.floor((Math.random() * this.maxExpire) + this.minExpire) * 1e3);
34 |
35 | // players who already caught this pkmn
36 | this.hasCatched = [];
37 |
38 | // players who already have seen this pkmn (to store cp)
39 | this.hasSeen = [];
40 |
41 | }
42 |
43 | /**
44 | * @param {Player} player
45 | * @return {Boolean}
46 | */
47 | caughtBy(player) {
48 | if (!this.alreadyCatchedBy(player)) {
49 | this.hasCatched.push(player.uid);
50 | }
51 | }
52 |
53 | /**
54 | * @param {Player} player
55 | * @return {Boolean}
56 | */
57 | alreadyCatchedBy(player) {
58 | return (
59 | this.hasCatched.indexOf(player.uid) > -1
60 | );
61 | }
62 |
63 | /**
64 | * @param {Player} player
65 | * @return {Number}
66 | */
67 | getSeenCp(player) {
68 | for (let item of this.hasSeen) {
69 | if (item.uid === player.uid) return (item.cp);
70 | };
71 | return (-1);
72 | }
73 |
74 | /**
75 | * @param {Player} player
76 | * @return {Boolean}
77 | */
78 | seenBy(player) {
79 | if (!this.alreadySeenBy(player)) {
80 | this.calcStats(player);
81 | this.hasSeen.push({
82 | cp: this.cp,
83 | uid: player.uid
84 | });
85 | }
86 | }
87 |
88 | /**
89 | * @param {Player} player
90 | * @return {Boolean}
91 | */
92 | alreadySeenBy(player) {
93 | for (let item of this.hasSeen) {
94 | if (item.uid === player.uid) return (true);
95 | };
96 | return (false);
97 | }
98 |
99 | /**
100 | * @return {Boolean}
101 | */
102 | isExpired() {
103 | return (
104 | +new Date() - this.creation >= this.expiration
105 | );
106 | }
107 |
108 | /**
109 | * @return {Number}
110 | */
111 | getEncounterId() {
112 | return (this.uid);
113 | }
114 |
115 | /**
116 | * @return {String}
117 | */
118 | getPkmnId() {
119 | return (
120 | this.getPkmnName().toUpperCase()
121 | );
122 | }
123 |
124 | /**
125 | * @return {Object}
126 | */
127 | serializeWild() {
128 | return ({
129 | encounter_id: this.encounterId,
130 | last_modified_timestamp_ms: this.creation,
131 | latitude: this.latitude,
132 | longitude: this.longitude,
133 | spawn_point_id: this.spawnPointId,
134 | pokemon_data: {
135 | pokemon_id: this.getPkmnId(),
136 | cp: this.cp,
137 | stamina: 10,
138 | stamina_max: 10,
139 | move_1: "BUG_BITE_FAST",
140 | move_2: "STRUGGLE",
141 | height_m: 0.30962005257606506,
142 | weight_kg: 3.3212273120880127,
143 | individual_attack: 7,
144 | individual_defense: 13,
145 | individual_stamina: 3,
146 | cp_multiplier: 0.16639786958694458
147 | },
148 | time_till_hidden_ms: this.expiration
149 | });
150 | }
151 |
152 | /**
153 | * @return {Object}
154 | */
155 | serializeCatchable() {
156 | return ({
157 | encounter_id: this.encounterId,
158 | pokemon_id: this.getPkmnId(),
159 | expiration_timestamp_ms: this.creation + this.expiration,
160 | latitude: this.latitude,
161 | longitude: this.longitude
162 | });
163 | }
164 |
165 | /**
166 | * @return {Object}
167 | */
168 | serializeNearby() {
169 | return ({
170 | pokemon_id: this.getPkmnId(),
171 | encounter_id: this.getEncounterId()
172 | });
173 | }
174 |
175 | }
--------------------------------------------------------------------------------
/src/models/Pokemon/action.js:
--------------------------------------------------------------------------------
1 | import print from "../../print";
2 |
3 | import {
4 | _toCC,
5 | } from "../../utils";
6 |
7 | const pokename = require("pokename")();
8 |
9 | /**
10 | * @return {Boolean}
11 | */
12 | export function powerUp() {
13 | if (this.hasReachedMaxLevel()) {
14 | print(`${this.owner.username}'s ${this.getPkmnName()} already reached maximum level!`);
15 | return void 0;
16 | }
17 | let pkmnTmpl = this.getPkmnTemplate(this.dexNumber);
18 | let ownerStardust = this.owner.info.stardust;
19 | let ownerPkmnCandies = this.owner.candyBag.getCandy(this.dexNumber);
20 | let requiredCandies = 1;
21 | print(`${this.getPkmnName()} requires ${requiredCandies} candies to power up!`);
22 | if (ownerPkmnCandies >= requiredCandies) {
23 | this.level += 1;
24 | this.owner.candyBag.removeCandy(this.dexNumber, requiredCandies);
25 | return (true);
26 | }
27 | return (false);
28 | }
29 |
30 | /**
31 | * @return {Boolean}
32 | */
33 | export function evolve() {
34 | let pkmnTmpl = this.getPkmnTemplate(this.dexNumber);
35 | let ownerPkmnCandies = this.owner.candyBag.getCandy(this.dexNumber);
36 | let candiesToEvolve = this.candiesToEvolve();
37 | if (ownerPkmnCandies < candiesToEvolve()) {
38 | return print(`You have ${ownerPkmnCandies}/${candiesToEvolve} candies to evolve ${this.getPkmnName()}!`, 31);
39 | }
40 | let evolutions = pkmnTmpl.evolution_ids;
41 | if (this.hasEvolution() && evolutions.length <= 1) {
42 | let result = this.evolveInto(evolutions[0]);
43 | this.owner.candyBag.removeCandy(this.dexNumber, pkmnTmpl.candy_to_evolve);
44 | return (result);
45 | }
46 | else {
47 | print(`Evolving this pokemon isnt supported yet!`, 31);
48 | }
49 | return (false);
50 | }
51 |
52 | /**
53 | * @param {String} ev
54 | * @return {Boolean}
55 | */
56 | export function evolveInto(ev) {
57 | let evName = _toCC(ev);
58 | let evId = pokename.getPokemonIdByName(evName);
59 | if (evId <= 0) {
60 | print(`Failed at retrieving id for pokemon ${ev}`, 31);
61 | return (false);
62 | }
63 | let evTmpl = this.getPkmnTemplate(evId);
64 | print(`${this.owner.username} successfully evolved ${this.getPkmnName()} into ${evName}`);
65 | return (true);
66 | }
--------------------------------------------------------------------------------
/src/models/Pokemon/calc.js:
--------------------------------------------------------------------------------
1 | import Settings from "../../modes";
2 |
3 | /**
4 | * @param {Player} owner
5 | */
6 | export function calcStats(owner) {
7 |
8 | let pkmnTmpl = this.getPkmnTemplate(this.dexNumber);
9 | let stats = pkmnTmpl.stats;
10 |
11 | let minIV = Settings.PKMN_SETTINGS.MIN_IV;
12 | let maxIV = Settings.PKMN_SETTINGS.MAX_IV;
13 |
14 | this.attack = stats.base_attack;
15 | this.defense = stats.base_defense;
16 | this.stamina = stats.base_stamina;
17 | this.staminaMax = this.stamina;
18 |
19 | this.ivAttack = ~~(Math.random() * maxIV) + minIV;
20 | this.ivDefense = ~~(Math.random() * maxIV) + minIV;
21 | this.ivStamina = ~~(Math.random() * maxIV) + minIV;
22 |
23 | this.height = pkmnTmpl.pokedex_height_m + ((Math.random() * pkmnTmpl.height_std_dev) + .1);
24 | this.weight = pkmnTmpl.pokedex_weight_kg + ((Math.random() * pkmnTmpl.weight_std_dev) + .1);
25 |
26 | if (owner !== null) {
27 | this.cp = Math.floor(Math.random() * this.calcCP(owner)) + 16;
28 | }
29 |
30 | this.calcMoves();
31 |
32 | }
33 |
34 | export function calcMoves() {
35 |
36 | let pkmnTmpl = this.getPkmnTemplate(this.dexNumber);
37 |
38 | let weakMoves = pkmnTmpl.quick_moves;
39 | let strongMoves = pkmnTmpl.cinematic_moves;
40 |
41 | this.move1 = weakMoves[(Math.random() * weakMoves.length) << 0];
42 | this.move2 = strongMoves[(Math.random() * strongMoves.length) << 0];
43 |
44 | }
45 |
46 | /**
47 | * @param {Player} owner
48 | * @return {Number}
49 | */
50 | export function calcCP(owner) {
51 |
52 | let levelSettings = owner.info.getLevelSettings();
53 | let ecpm = levelSettings.cp_multiplier[owner.info.level - 1];
54 |
55 | let atk = (this.attack + this.ivAttack) * ecpm;
56 | let def = (this.defense + this.ivDefense) * ecpm;
57 | let sta = (this.stamina + this.ivStamina) * ecpm;
58 |
59 | return (
60 | Math.max(10, Math.floor(Math.sqrt(atk * atk * def * sta) / 10))
61 | );
62 |
63 | }
64 |
65 | /**
66 | * @param {Number} lvl
67 | * @return {Number}
68 | */
69 | export function getHalfLevelCPMultiplier(lvl) {
70 | let next = this.getCPMultipliers()[lvl + 1];
71 | return (
72 | Math.sqrt(Math.pow(lvl, 2) + ((Math.pow(next, 2) - Math.pow(lvl, 2)) / 2))
73 | );
74 | }
--------------------------------------------------------------------------------
/src/models/Pokemon/index.js:
--------------------------------------------------------------------------------
1 | import MapObject from "../World/MapObject";
2 |
3 | import { GAME_MASTER } from "../../shared";
4 |
5 | import Settings from "../../modes";
6 |
7 | import {
8 | _toCC,
9 | inherit,
10 | deCapitalize,
11 | validUsername
12 | } from "../../utils";
13 |
14 | import print from "../../print";
15 |
16 | import CFG from "../../../cfg";
17 | import ENUM from "../../enum";
18 |
19 | import * as _calc from "./calc";
20 | import * as _actions from "./action";
21 |
22 | const pokename = require("pokename")();
23 |
24 | /**
25 | * @class Pokemon
26 | */
27 | export default class Pokemon extends MapObject {
28 |
29 | /**
30 | * @param {Object} obj
31 | * @constructor
32 | */
33 | constructor(obj) {
34 |
35 | super(null);
36 |
37 | this.dexNumber = 0;
38 |
39 | this._level = 1;
40 | this.capturedLevel = 0;
41 |
42 | this.cp = 0;
43 | this.cpMultiplier = Math.random() + 1.0;
44 | this.addCpMultiplier = 0;
45 |
46 | this.move1 = 0;
47 | this.move2 = 0;
48 |
49 | this.attack = 0;
50 | this.defense = 0;
51 | this.stamina = 0;
52 |
53 | this.height = 0;
54 | this.weight = 0;
55 |
56 | this.ivAttack = 0;
57 | this.ivDefense = 0;
58 | this.ivStamina = 0;
59 |
60 | this.staminaMax = 0;
61 |
62 | this.favorite = 0;
63 |
64 | this.owner = null;
65 | this.nickname = null;
66 | this.pokeball = null;
67 |
68 | this.isWild = false;
69 | this.isOwned = false;
70 |
71 | this.spawnPoint = null;
72 |
73 | this.init(obj);
74 |
75 | }
76 |
77 | get level() {
78 | return (this._level + 1);
79 | }
80 | set level(value) {
81 | this._level = parseFloat(value);
82 | }
83 |
84 | /**
85 | * @param {Object} obj
86 | */
87 | init(obj) {
88 | obj = obj || {};
89 | for (let key in obj) {
90 | if (this.hasOwnProperty(key)) {
91 | this[key] = obj[key];
92 | }
93 | else if (this.hasOwnProperty(this.normalizeKey(key))) {
94 | this[this.normalizeKey(key)] = obj[key];
95 | }
96 | else if (key === "id") {
97 | this.uid = parseInt(obj[key]);
98 | }
99 | else if (key === "move_1") {
100 | this.move1 = obj[key];
101 | }
102 | else if (key === "move_2") {
103 | this.move2 = obj[key];
104 | }
105 | else if (key === "height_m") {
106 | this.height = obj[key];
107 | }
108 | else if (key === "weight_kg") {
109 | this.weight = obj[key];
110 | }
111 | else if (key === "individual_attack") {
112 | this.ivAttack = obj[key];
113 | }
114 | else if (key === "individual_defense") {
115 | this.ivDefense = obj[key];
116 | }
117 | else if (key === "individual_stamina") {
118 | this.ivStamina = obj[key];
119 | }
120 | };
121 | if (!obj.isWild && !obj.isOwned) this.calcStats(this.owner);
122 | }
123 |
124 | /**
125 | * @param {String} key
126 | * @return {String}
127 | */
128 | normalizeKey(key) {
129 | return (
130 | deCapitalize(_toCC(key))
131 | );
132 | }
133 |
134 | /**
135 | * @param {Boolean} truth
136 | */
137 | setFavorite(truth) {
138 | this.favorite = !!truth;
139 | }
140 |
141 | /**
142 | * @param {String} name
143 | */
144 | setNickname(name) {
145 | if (validUsername(name)) {
146 | this.nickname = name;
147 | }
148 | }
149 |
150 | /**
151 | * @param {Number} dex
152 | * @return {Object}
153 | */
154 | getPkmnTemplate(dex) {
155 | let tmpl = GAME_MASTER.getPokemonTmplByDex(dex);
156 | return (tmpl);
157 | }
158 |
159 | /**
160 | * @return {String}
161 | */
162 | getPkmnName() {
163 | return (
164 | pokename.getPokemonNameById(this.dexNumber)
165 | );
166 | }
167 |
168 | /**
169 | * @param {Number} dex
170 | * @return {String}
171 | */
172 | getPkmnFamily(dex) {
173 | return (
174 | this.getPkmnTemplate(dex).family_id
175 | );
176 | }
177 |
178 | /**
179 | * @param {Number} amount
180 | */
181 | addCandies(amount) {
182 | let family = this.getPkmnFamily(this.dexNumber);
183 | let id = ENUM.getIdByName(ENUM.POKEMON_FAMILY, family) << 0;
184 | if (this.owner) this.owner.candyBag.addCandy(id, parseInt(amount));
185 | }
186 |
187 | /**
188 | * @return {Boolean}
189 | */
190 | hasEvolution() {
191 | let pkmnTmpl = this.getPkmnTemplate(this.dexNumber);
192 | return (
193 | pkmnTmpl.evolution_ids.length >= 1
194 | );
195 | }
196 |
197 | /**
198 | * @return {Number}
199 | */
200 | candiesToEvolve() {
201 | let pkmnTmpl = this.getPkmnTemplate(this.dexNumber);
202 | return (pkmnTmpl.candy_to_evolve << 0);
203 | }
204 |
205 | /**
206 | * @return {Boolean}
207 | */
208 | hasReachedMaxLevel() {
209 | return (
210 | this.level > this.owner.info.getMaximumLevel() * 2
211 | );
212 | }
213 |
214 | insertIntoDatabase() {
215 | let query = `
216 | INSERT INTO ${CFG.MYSQL_OWNED_PKMN_TABLE} SET
217 | owner_id=?,
218 | dex_number=?,
219 | cp=?,
220 | stamina=?,
221 | stamina_max=?,
222 | move_1=?,
223 | move_2=?,
224 | height_m=?,
225 | weight_kg=?,
226 | individual_attack=?,
227 | individual_defense=?,
228 | individual_stamina=?,
229 | cp_multiplier=?,
230 | pokeball=?,
231 | favorite=?,
232 | nickname=?
233 | `;
234 | let data = [
235 | this.owner.uid, this.dexNumber, this.cp,
236 | this.stamina, this.staminaMax,
237 | this.move1, this.move2,
238 | this.height, this.weight,
239 | this.ivAttack, this.ivDefense, this.ivStamina,
240 | this.cpMultiplier, this.pokeball, this.favorite, this.nickname || ""
241 | ];
242 | return new Promise((resolve) => {
243 | this.owner.world.db.query(query, data, (e, res) => {
244 | if (e) return print(e, 31);
245 | resolve(res.insertId);
246 | });
247 | });
248 | }
249 |
250 | deleteFromDatabase() {
251 | let query = `DELETE FROM ${CFG.MYSQL_OWNED_PKMN_TABLE} WHERE id=? AND owner_id=? LIMIT 1`;
252 | return new Promise((resolve) => {
253 | this.owner.world.db.query(query, [this.uid, this.owner.uid], (e, res) => {
254 | if (e) return print(e, 31);
255 | resolve(res);
256 | });
257 | });
258 | }
259 |
260 | /**
261 | * @return {Object}
262 | */
263 | serialize() {
264 | return ({
265 | id: this.uid,
266 | pokemon_id: this.dexNumber,
267 | cp: this.cp,
268 | stamina: this.stamina,
269 | stamina_max: this.staminaMax,
270 | move_1: this.move1,
271 | move_2: this.move2,
272 | height_m: this.height,
273 | weight_kg: this.weight,
274 | individual_attack: this.ivAttack,
275 | individual_defense: this.ivDefense,
276 | individual_stamina: this.ivStamina,
277 | cp_multiplier: this.cpMultiplier,
278 | pokeball: "ITEM_POKE_BALL",
279 | captured_cell_id: "1337",
280 | creation_time_ms: +new Date(),
281 | favorite: this.favorite,
282 | nickname: this.nickname
283 | });
284 | }
285 |
286 | /**
287 | * @return {Array}
288 | */
289 | querify() {
290 | return ([
291 |
292 | ]);
293 | }
294 |
295 | }
296 |
297 | inherit(Pokemon, _calc);
298 | inherit(Pokemon, _actions);
--------------------------------------------------------------------------------
/src/models/World/Cell/index.js:
--------------------------------------------------------------------------------
1 | import s2 from "s2-geometry";
2 |
3 | import Gym from "../Fort/Gym";
4 | import Pokestop from "../Fort/Pokestop";
5 | import MapObject from "../MapObject";
6 | import SpawnPoint from "../SpawnPoint";
7 | import WildPokemon from "../../Pokemon/WildPokemon";
8 |
9 | import Settings from "../../../modes";
10 | import CFG from "../../../../cfg";
11 |
12 | import print from "../../../print";
13 |
14 | const S2Geo = s2.S2;
15 | const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds;
16 |
17 | /**
18 | * @class Cell
19 | */
20 | export default class Cell extends MapObject {
21 |
22 | /**
23 | * @param {Object} obj
24 | * @constructor
25 | */
26 | constructor(obj) {
27 |
28 | super(obj);
29 |
30 | this.forts = [];
31 |
32 | this.type = obj.type;
33 |
34 | this.synced = false;
35 |
36 | this.expiration = 0;
37 |
38 | }
39 |
40 | /**
41 | * @param {Number} lat
42 | * @param {Number} lng
43 | * @return {String}
44 | */
45 | static getIdByPosition(lat, lng, zoom) {
46 | return (
47 | S2Geo.keyToId(S2Geo.latLngToKey(lat, lng, zoom || 15))
48 | );
49 | }
50 |
51 | delete() {
52 | let index = this.world.getCellIndexByCellId(this.cellId);
53 | let cell = this.world.cells[index];
54 | if (cell) {
55 | this.world.cells.splice(index, 1);
56 | print(`Cell ${cell.cellId} timed out!`, 33);
57 | }
58 | }
59 |
60 | /**
61 | * @param {Object} obj
62 | * @return {Fort}
63 | */
64 | addFort(obj) {
65 | obj.world = this.world;
66 | let fort = (
67 | obj.type === "CHECKPOINT" ? new Pokestop(obj) :
68 | obj.type === "SPAWN" ? new SpawnPoint(obj) :
69 | new Gym(obj)
70 | );
71 | this.forts.push(fort);
72 | return (fort);
73 | }
74 |
75 | refreshSpawnPoints() {
76 | this.forts.map((fort) => {
77 | if (fort.isSpawn === true) {
78 | fort.refresh();
79 | }
80 | });
81 | }
82 |
83 | loadForts() {
84 | return new Promise((resolve) => {
85 | this.expiration = +new Date() + CFG.CELL_TIMEOUT;
86 | if (this.synced) {
87 | this.forts.map((fort) => {
88 | this.processDeletedFort(fort);
89 | });
90 | resolve(this.forts);
91 | }
92 | else {
93 | this.getFortsFromDatabase().then((forts) => {
94 | this.forts = [];
95 | forts.map((fort) => {
96 | this.processDeletedFort(this.addFort(fort));
97 | });
98 | this.synced = true;
99 | print(`Synced ${this.cellId} with database..`, 33);
100 | resolve(this.forts);
101 | });
102 | }
103 | });
104 | }
105 |
106 | /**
107 | * @param {String} type
108 | * @return {String}
109 | */
110 | static getFortTable(type) {
111 | return (
112 | type === "CHECKPOINT" ?
113 | CFG.MYSQL_POKESTOP_TABLE :
114 | type === "SPAWN" ?
115 | CFG.MYSQL_SPAWN_TABLE :
116 | type === "GYM" ?
117 | CFG.MYSQL_GYM_TABLE :
118 | "INVALID"
119 | );
120 | }
121 |
122 | getSpawnsFromDatabase() {
123 | return new Promise((resolve) => {
124 | this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_SPAWN_TABLE).then((spawns) => {
125 | resolve(spawns || []);
126 | });
127 | });
128 | }
129 |
130 | getFortsFromDatabase() {
131 | return new Promise((resolve) => {
132 | let out = [];
133 | this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_POKESTOP_TABLE).then((forts) => {
134 | forts = forts || [];
135 | forts.map((fort) => {
136 | fort.type = "CHECKPOINT";
137 | out.push(fort);
138 | });
139 | this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_GYM_TABLE).then((forts) => {
140 | forts = forts || [];
141 | forts.map((fort) => {
142 | fort.type = "GYM";
143 | out.push(fort);
144 | });
145 | this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_SPAWN_TABLE).then((forts) => {
146 | forts = forts || [];
147 | forts.map((fort) => {
148 | fort.type = "SPAWN";
149 | out.push(fort);
150 | });
151 | resolve(out);
152 | });
153 | });
154 | });
155 | });
156 | }
157 |
158 | /**
159 | * Dirty hack to display disappearing forts
160 | * seems like game engine doesnt support dat
161 | * @param {Fort} fort
162 | */
163 | processDeletedFort(fort) {
164 | if (fort.deleted) {
165 | fort.latitude = 0;
166 | fort.longitude = 0;
167 | fort.enabled = false;
168 | // wait for the next map refresh
169 | setTimeout(() => {
170 | this.deleteFortById(fort.uid);
171 | this.deleteFortFromDatabase(fort).then(() => {
172 | // do sth after
173 | });
174 | }, (MAP_REFRESH_RATE * 1e3) * 3);
175 | }
176 | }
177 |
178 | /**
179 | * @param {Fort} fort
180 | */
181 | deleteFortFromDatabase(fort) {
182 | return new Promise((resolve) => {
183 | let table = Cell.getFortTable(fort.type);
184 | this.world.instance.db.query(`DELETE FROM ${table} WHERE cell_id=? AND id=? LIMIT 1`, [fort.cellId, fort.uid], (e, res) => {
185 | resolve();
186 | });
187 | });
188 | }
189 |
190 | /**
191 | * @param {Number} id
192 | * @return {Number}
193 | */
194 | getFortIndexById(id) {
195 | let index = 0;
196 | for (let fort of this.forts) {
197 | if (fort.uid === id) return (index);
198 | ++index;
199 | };
200 | return (-1);
201 | }
202 |
203 | /**
204 | * @param {Number} id
205 | * @return {Fort}
206 | */
207 | getFortById(id) {
208 | let index = this.getFortIndexById(id);
209 | if (index < 0) return (null);
210 | return (this.forts[index]);
211 | }
212 |
213 | /**
214 | * @param {Number} id
215 | * @return {Fort}
216 | */
217 | deleteFortById(id) {
218 | let index = this.getFortIndexById(id);
219 | if (index < 0) return (null);
220 | return (this.forts.splice(index, 1));
221 | }
222 |
223 | /**
224 | * @return {Array}
225 | */
226 | serializeSpawnPoints() {
227 | let ii = 0;
228 | let length = this.forts.length;
229 | let out = [];
230 | let fort = null;
231 | for (; ii < length; ++ii) {
232 | fort = this.forts[ii];
233 | if (!(fort.isSpawn === true)) continue;
234 | out.push(fort.serialize());
235 | };
236 | return (out);
237 | }
238 |
239 | /**
240 | * @param {Player} player
241 | * @return {Array}
242 | */
243 | serializePkmns(player) {
244 | let ii = 0;
245 | let length = this.forts.length;
246 | let out = {
247 | wild: [],
248 | nearby: [],
249 | catchable: []
250 | };
251 | let fort = null;
252 | for (; ii < length; ++ii) {
253 | fort = this.forts[ii];
254 | if (!(fort.isSpawn === true)) continue;
255 | fort.activeSpawns.map((encounter) => {
256 | if (
257 | !encounter.alreadyCatchedBy(player) &&
258 | !encounter.isDespawned
259 | ) {
260 | out.wild.push(encounter.serializeWild());
261 | out.nearby.push(encounter.serializeNearby());
262 | out.catchable.push(encounter.serializeCatchable());
263 | }
264 | });
265 | };
266 | return (out);
267 | }
268 |
269 | /**
270 | * @param {Player} player
271 | * @return {Object}
272 | */
273 | serialize(player) {
274 | let buffer = {
275 | s2_cell_id: this.cellId,
276 | current_timestamp_ms: +new Date(),
277 | forts: this.forts.map((fort) => { if (!fort.isSpawn) return fort.serialize(); }),
278 | spawn_points: this.serializeSpawnPoints(),
279 | deleted_objects: [],
280 | fort_summaries: [],
281 | decimated_spawn_points: [],
282 | wild_pokemons: null,
283 | catchable_pokemons: null,
284 | nearby_pokemons: null
285 | };
286 | let pkmns = this.serializePkmns(player);
287 | buffer.wild_pokemons = pkmns.wild;
288 | buffer.nearby_pokemons = pkmns.nearby;
289 | buffer.catchable_pokemons = pkmns.catchable;
290 | return (buffer);
291 | }
292 |
293 | }
--------------------------------------------------------------------------------
/src/models/World/Fort/Gym/index.js:
--------------------------------------------------------------------------------
1 | import Fort from "../index";
2 |
3 | import print from "../../../../print";
4 | import CFG from "../../../../../cfg";
5 |
6 | import {
7 | validName
8 | } from "../../../../utils";
9 |
10 | import ENUM from "../../../../enum";
11 |
12 | /**
13 | * @class Gym
14 | */
15 | export default class Gym extends Fort {
16 |
17 | /**
18 | * @param {Object} obj
19 | * @constructor
20 | */
21 | constructor(obj) {
22 |
23 | super(obj);
24 |
25 | this.team = 0;
26 |
27 | this.guardPkmn = 0;
28 | this.guardPkmnCp = 0;
29 |
30 | this.gymPoints = 0;
31 |
32 | this.isInBattle = 0;
33 |
34 | this.init(obj);
35 |
36 | }
37 |
38 | /**
39 | * @return {Object}
40 | */
41 | serialize() {
42 | return ({
43 | id: this.getId(),
44 | last_modified_timestamp_ms: +new Date(),
45 | latitude: this.latitude,
46 | longitude: this.longitude,
47 | enabled: this.enabled,
48 | owned_by_team: ENUM.getNameById(ENUM.TEAM, this.team),
49 | guard_pokemon_id: this.guardPkmn,
50 | gym_points: this.gymPoints,
51 | active_fort_modifier: []
52 | });
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/src/models/World/Fort/Pokestop/index.js:
--------------------------------------------------------------------------------
1 | import Fort from "../index";
2 |
3 | import print from "../../../../print";
4 | import CFG from "../../../../../cfg";
5 |
6 | import {
7 | validName
8 | } from "../../../../utils";
9 |
10 | import ENUM from "../../../../enum";
11 |
12 | /**
13 | * @class Gym
14 | */
15 | export default class Pokestop extends Fort {
16 |
17 | /**
18 | * @param {Object} obj
19 | * @constructor
20 | */
21 | constructor(obj) {
22 |
23 | super(obj);
24 |
25 | this.name = null;
26 | this.description = null;
27 |
28 | this.image_url = null;
29 |
30 | this.cooldown = 10e3;
31 |
32 | this.experience = 0;
33 |
34 | this.rewards = this.parseRewards(obj.rewards);
35 |
36 | this.init(obj);
37 |
38 | }
39 |
40 | /**
41 | * @return {Object}
42 | */
43 | parseRewards(rewards) {
44 | rewards = rewards || "{}";
45 | let json = null;
46 | try {
47 | json = JSON.parse(rewards);
48 | } catch (e) {
49 | json = {};
50 | print(e, 31);
51 | }
52 | return (json);
53 | }
54 |
55 | /**
56 | * @return {Array}
57 | */
58 | serializeRewards() {
59 | let out = [];
60 | for (let key in this.rewards) {
61 | let amount = this.rewards[key] << 0;
62 | for (let ii = 0; ii < amount; ++ii) {
63 | out.push({
64 | item_id: ENUM.getNameById(ENUM.ITEMS, key << 0)
65 | });
66 | };
67 | };
68 | return (out);
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/models/World/Fort/index.js:
--------------------------------------------------------------------------------
1 | import MapObject from "../MapObject";
2 |
3 | import print from "../../../print";
4 | import CFG from "../../../../cfg";
5 |
6 | import {
7 | validName
8 | } from "../../../utils";
9 |
10 | /**
11 | * @class Fort
12 | */
13 | export default class Fort extends MapObject {
14 |
15 | /**
16 | * @param {Object} obj
17 | * @constructor
18 | */
19 | constructor(obj) {
20 |
21 | super(obj);
22 |
23 | this.enabled = true;
24 | this.deleted = false;
25 |
26 | this.type = null;
27 |
28 | this.init(obj);
29 |
30 | this.uid += this.type[0].toUpperCase();
31 |
32 | }
33 |
34 | /**
35 | * @param {Object} obj
36 | */
37 | init(obj) {
38 | obj = obj || {};
39 | for (let key in obj) {
40 | if (this.hasOwnProperty(key) && key !== "rewards") {
41 | this[key] = obj[key];
42 | }
43 | };
44 | }
45 |
46 | /**
47 | * @return {String}
48 | */
49 | getId() {
50 | return (
51 | this.cellId + "." + this.uid
52 | );
53 | }
54 |
55 | delete() {
56 | this.deleted = true;
57 | }
58 |
59 | /**
60 | * @return {Object}
61 | */
62 | serialize() {
63 | return ({
64 | id: this.getId(),
65 | last_modified_timestamp_ms: +new Date(),
66 | latitude: this.latitude,
67 | longitude: this.longitude,
68 | enabled: this.enabled,
69 | type: this.type
70 | });
71 | }
72 |
73 | }
--------------------------------------------------------------------------------
/src/models/World/MapObject/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class MapObject
3 | */
4 | export default class MapObject {
5 |
6 | /**
7 | * @param {Object} obj
8 | * @constructor
9 | */
10 | constructor(obj) {
11 |
12 | this.uid = 0;
13 |
14 | this.cellId = null;
15 |
16 | this.world = null;
17 |
18 | this.altitude = 0;
19 | this.latitude = 0;
20 | this.longitude = 0;
21 |
22 | this._init(obj);
23 |
24 | }
25 |
26 | /**
27 | * @param {Object} obj
28 | */
29 | _init(obj) {
30 | obj = obj || {};
31 | for (let key in obj) {
32 | if (this.hasOwnProperty(key)) {
33 | this[key] = obj[key];
34 | }
35 | else if (key === "id") {
36 | this.uid = obj[key];
37 | }
38 | else if (key === "cell_id") {
39 | this.cellId = obj[key];
40 | }
41 | };
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/src/models/World/SpawnPoint/index.js:
--------------------------------------------------------------------------------
1 | import rare from "pokerare";
2 |
3 | import MapObject from "../MapObject";
4 | import WildPokemon from "../../Pokemon/WildPokemon";
5 |
6 | import print from "../../../print";
7 | import CFG from "../../../../cfg";
8 |
9 | import Settings from "../../../modes";
10 |
11 | const pokename = require("pokename")();
12 |
13 | const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds;
14 |
15 | /**
16 | * @class SpawnPoint
17 | */
18 | export default class SpawnPoint extends MapObject {
19 |
20 | /**
21 | * @param {Object} obj
22 | * @constructor
23 | */
24 | constructor(obj) {
25 |
26 | super(obj);
27 |
28 | this.type = null;
29 |
30 | this.range = 3;
31 |
32 | this.spawns = JSON.parse(obj.encounters);
33 |
34 | this.minExpire = ((obj.min_spawn_expire) * 60) << 0;
35 | this.maxExpire = ((obj.max_spawn_expire) * 60) << 0;
36 |
37 | this.isSpawn = true;
38 |
39 | this.activeSpawns = [];
40 |
41 | this.init(obj);
42 |
43 | this.uid += this.type[0].toUpperCase();
44 |
45 | }
46 |
47 | /**
48 | * @param {Object} obj
49 | */
50 | init(obj) {
51 | obj = obj || {};
52 | for (let key in obj) {
53 | if (this.hasOwnProperty(key)) {
54 | this[key] = obj[key];
55 | }
56 | };
57 | }
58 |
59 | refresh() {
60 | this.activeSpawns.map((pkmn) => {
61 | if (pkmn.isExpired() && !pkmn.isDespawned) {
62 | pkmn.isDespawned = true;
63 | pkmn.despawnIn = +new Date() + ((MAP_REFRESH_RATE * 1e3) + pkmn.expiration);
64 | }
65 | else if (pkmn.isDespawned) {
66 | if (+new Date() >= pkmn.despawnIn) {
67 | this.despawnPkmn(pkmn);
68 | }
69 | }
70 | });
71 | }
72 |
73 | /**
74 | * @return {Object}
75 | */
76 | getRandomPosition() {
77 | let range = this.range / 1e4;
78 | let latitude = this.latitude + (Math.random() * (range * 2)) - range;
79 | let longitude = this.longitude + (Math.random() * (range * 2)) - range;
80 | return ({
81 | lat: latitude,
82 | lng: longitude
83 | });
84 | }
85 |
86 | spawnPkmn() {
87 | let randId = this.spawns[Math.floor(Math.random() * this.spawns.length)];
88 | let randPos = this.getRandomPosition();
89 | let pkmn = new WildPokemon({
90 | dexNumber: randId,
91 | latitude: randPos.lat,
92 | longitude: randPos.lng,
93 | isWild: true,
94 | cellId: this.cellId,
95 | spawnPointId: this.uid,
96 | minExpire: this.minExpire,
97 | maxExpire: this.maxExpire
98 | });
99 | this.activeSpawns.push(pkmn);
100 | print(`Spawned 1x ${pkmn.getPkmnName()}:${pkmn.uid} at ${this.cellId}`);
101 | }
102 |
103 | /**
104 | * @param {WildPokemon} pkmn
105 | */
106 | despawnPkmn(pkmn) {
107 | let index = 0;
108 | this.activeSpawns.map((encounter) => {
109 | if (encounter.uid === pkmn.uid) {
110 | print(`Killed 1x ${pkmn.getPkmnName()}:${pkmn.uid} at ${this.cellId}`, 33);
111 | this.activeSpawns.splice(index, 1);
112 | }
113 | index++;
114 | });
115 | }
116 |
117 | /**
118 | * @param {String} id
119 | * @return {WildPokemon}
120 | */
121 | getPkmnSpawnById(id) {
122 | let ii = 0;
123 | let length = this.activeSpawns.length;
124 | let spawn = null;
125 | for (; ii < length; ++ii) {
126 | spawn = this.activeSpawns[ii];
127 | if (spawn.uid === id) return (spawn);
128 | }
129 | return (null);
130 | }
131 |
132 | /**
133 | * @return {Object}
134 | */
135 | serialize() {
136 | return ({
137 | latitude: this.latitude,
138 | longitude: this.longitude
139 | });
140 | }
141 |
142 | }
--------------------------------------------------------------------------------
/src/models/World/forts.js:
--------------------------------------------------------------------------------
1 | import Cell from "./Cell";
2 |
3 | import CFG from "../../../cfg";
4 |
5 | import print from "../../print";
6 |
7 | /**
8 | * @param {String} id
9 | */
10 | export function getFortDataById(id) {
11 |
12 | id = id.split(".") || [];
13 |
14 | let uid = id[1];
15 | let cellId = id[0];
16 |
17 | let cell = this.getCellById(cellId);
18 |
19 | return new Promise((resolve) => {
20 | if (!cellId || !uid || !cell) return resolve();
21 | cell.loadForts().then(() => {
22 | let fort = cell.getFortById(uid);
23 | resolve(fort);
24 | });
25 | });
26 |
27 | }
28 |
29 | /**
30 | * @param {Array} cells
31 | * @param {Array} out
32 | * @param {Number} index
33 | */
34 | export function getFortsByCells(cells, out, index) {
35 | return new Promise((resolve) => {
36 | this.getFortsByCellId(cells[index]).then((result) => {
37 | out.push(result);
38 | if (++index >= cells.length) resolve(out);
39 | else resolve(this.getFortsByCells(cells, out, index));
40 | });
41 | });
42 | }
43 |
44 | /**
45 | * @param {String} cellId
46 | */
47 | export function getFortsByCellId(cellId) {
48 | return new Promise((resolve) => {
49 | if (!this.cellAlreadyRegistered(cellId)) {
50 | this.registerCell(cellId).then((cell) => {
51 | resolve(cell);
52 | });
53 | }
54 | else {
55 | let cell = this.getCellById(cellId);
56 | cell.loadForts().then(() => {
57 | resolve(cell);
58 | });
59 | }
60 | });
61 | }
62 |
63 | /**
64 | * @param {String} cellId
65 | */
66 | export function registerCell(cellId) {
67 | let cell = new Cell({
68 | world: this,
69 | cellId: cellId
70 | });
71 | this.cells.push(cell);
72 | return new Promise((resolve) => {
73 | cell.loadForts().then(() => {
74 | resolve(this.getCellById(cellId));
75 | });
76 | });
77 | }
78 |
79 | /**
80 | * @param {Object} obj
81 | */
82 | export function addFort(obj) {
83 | let cellId = Cell.getIdByPosition(obj.latitude, obj.longitude, obj.zoom);
84 | return new Promise((resolve) => {
85 | this.getFortsByCellId(cellId).then((cell) => {
86 | let fort = cell.addFort(obj);
87 | resolve(fort);
88 | });
89 | });
90 | }
91 |
92 | export function deleteFort(cellId, uid) {
93 | let cell = this.getCellById(cellId);
94 | let fort = cell.getFortById(uid);
95 | return new Promise((resolve) => {
96 | fort.delete();
97 | resolve();
98 | });
99 | }
100 |
101 | /**
102 | * @param {Object} obj
103 | */
104 | export function insertFortIntoDatabase(obj) {
105 | return new Promise((resolve) => {
106 | if (obj.type === "CHECKPOINT") {
107 | this.insertPokestopIntoDatabase(obj).then((gym) => {
108 | resolve(gym);
109 | });
110 | }
111 | else if (obj.type === "GYM") {
112 | this.insertGymIntoDatabase(obj).then((pokestop) => {
113 | resolve(pokestop);
114 | });
115 | }
116 | else if (obj.type === "SPAWN") {
117 | this.insertSpawnIntoDatabase(obj).then((spawn) => {
118 | resolve(spawn);
119 | });
120 | }
121 | });
122 | }
123 |
124 | export function insertPokestopIntoDatabase(obj) {
125 | let cellId = Cell.getIdByPosition(obj.latitude, obj.longitude, obj.zoom);
126 | let lat = obj.latitude;
127 | let lng = obj.longitude;
128 | let name = obj.name;
129 | let desc = obj.description;
130 | let img = obj.imageUrl || "";
131 | let exp = obj.experience || 500;
132 | let query = `INSERT INTO ${Cell.getFortTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, name=?, description=?, image_url=?, experience=?, rewards=?`;
133 | return new Promise((resolve) => {
134 | this.instance.db.query(query, [cellId, lat, lng, name, desc, img, exp, ""], (e, res) => {
135 | let insertId = res.insertId;
136 | obj.uid = insertId;
137 | obj.cell_id = cellId;
138 | this.addFort(obj).then((fort) => {
139 | resolve(fort);
140 | });
141 | });
142 | });
143 | }
144 |
145 | export function insertGymIntoDatabase(obj) {
146 | let cellId = Cell.getIdByPosition(obj.latitude, obj.longitude, obj.zoom);
147 | let lat = obj.latitude;
148 | let lng = obj.longitude;
149 | let query = `INSERT INTO ${Cell.getFortTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, team=?, in_battle=?, points=?`;
150 | return new Promise((resolve) => {
151 | this.instance.db.query(query, [cellId, lat, lng, 0, 0, 0], (e, res) => {
152 | let insertId = res.insertId;
153 | obj.uid = insertId;
154 | obj.cell_id = cellId;
155 | this.addFort(obj).then((fort) => {
156 | resolve(fort);
157 | });
158 | });
159 | });
160 | }
161 |
162 | export function insertSpawnIntoDatabase(obj) {
163 | let cellId = Cell.getIdByPosition(obj.latitude, obj.longitude, obj.zoom);
164 | let lat = obj.latitude;
165 | let lng = obj.longitude;
166 | let encounters = [];
167 | obj.encounters.split(",").map((encounter) => {
168 | encounters.push(encounter << 0);
169 | });
170 | let query = `INSERT INTO ${Cell.getFortTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, encounters=?, update_interval=?`;
171 | return new Promise((resolve) => {
172 | this.instance.db.query(query, [cellId, lat, lng, `[${encounters}]`, obj.interval << 0], (e, res) => {
173 | obj.uid = res.insertId;
174 | obj.cell_id = cellId;
175 | this.addFort(obj).then((fort) => {
176 | resolve(fort);
177 | });
178 | });
179 | });
180 | }
181 |
--------------------------------------------------------------------------------
/src/models/World/index.js:
--------------------------------------------------------------------------------
1 | import Cell from "./Cell";
2 |
3 | import CFG from "../../../cfg";
4 |
5 | import print from "../../print";
6 |
7 | import * as _forts from "./forts";
8 | import * as _players from "./players";
9 | import * as _packets from "./packets";
10 |
11 | import { inherit } from "../../utils";
12 |
13 | /**
14 | * @class World
15 | */
16 | export default class World {
17 |
18 | /** @constructor */
19 | constructor(instance) {
20 |
21 | this.instance = instance;
22 |
23 | this.db = this.instance.db;
24 |
25 | this.players = [];
26 |
27 | this.cells = [];
28 |
29 | }
30 |
31 | get connectedPlayers() {
32 | return (this.players.length);
33 | }
34 |
35 | /**
36 | * @param {String} cellId
37 | * @return {Number}
38 | */
39 | getCellIndexByCellId(cellId) {
40 | let ii = 0;
41 | let length = this.cells.length;
42 | for (; ii < length; ++ii) {
43 | if (this.cells[ii].cellId === cellId) return (ii);
44 | };
45 | return (-1);
46 | }
47 |
48 | /**
49 | * @param {String} cellId
50 | * @return {Cell}
51 | */
52 | getCellByCellId(cellId) {
53 | let index = this.getCellIndexByCellId(cellId);
54 | return (this.cells[index] || null);
55 | }
56 |
57 | /**
58 | * @param {String} cellId
59 | * @return {Boolean}
60 | */
61 | cellAlreadyRegistered(cellId) {
62 | let cell = this.getCellByCellId(cellId);
63 | return (
64 | cell !== null
65 | );
66 | }
67 |
68 | /**
69 | * @param {String} cellId
70 | * @return {Cell}
71 | */
72 | getCellById(cellId) {
73 | return (this.getCellByCellId(cellId));
74 | }
75 |
76 | refreshSpawns() {
77 | let ii = 0;
78 | let length = this.cells.length;
79 | for (; ii < length; ++ii) {
80 | this.cells[ii].refreshSpawnPoints();
81 | };
82 | }
83 |
84 | /**
85 | * @param {Number} lat
86 | * @param {Number} lng
87 | */
88 | triggerSpawnAt(lat, lng) {
89 | let cell = this.getCellById(Cell.getIdByPosition(lat, lng, 15));
90 | // Wait until cell got registered
91 | if (cell === null) return void 0;
92 | cell.forts.map((fort) => {
93 | if (fort.isSpawn) {
94 | if (fort.activeSpawns.length >= fort.spawns.length) return void 0;
95 | fort.spawnPkmn();
96 | }
97 | });
98 | }
99 |
100 | /**
101 | * @param {String} id
102 | * @return {WildPokemon}
103 | */
104 | getEncounterById(id) {
105 | id = parseInt(id);
106 | let ii = 0;
107 | let jj = 0;
108 | let fortLength = 0;
109 | let cellLength = this.cells.length;
110 | let cell = null;
111 | let fort = null;
112 | let pkmn = null;
113 | for (; ii < cellLength; ++ii) {
114 | cell = this.cells[ii];
115 | fortLength = cell.forts.length;
116 | for (; jj < fortLength; ++jj) {
117 | fort = cell.forts[jj];
118 | if (fort.isSpawn === true) {
119 | if ((pkmn = fort.getPkmnSpawnById(id)) !== null) {
120 | return (pkmn);
121 | }
122 | }
123 | };
124 | jj = 0;
125 | };
126 | return (pkmn);
127 | }
128 |
129 | /**
130 | * @param {String} type
131 | * @param {Object} msg
132 | */
133 | getPacket(type, msg) {
134 | return new Promise((resolve) => {
135 | switch (type) {
136 | case "ENCOUNTER":
137 | resolve(this.Encounter(msg));
138 | break;
139 | case "CATCH_POKEMON":
140 | resolve(this.CatchPokemon(msg));
141 | break;
142 | case "FORT_SEARCH":
143 | this.FortSearch(msg).then((result) => {
144 | resolve(result);
145 | });
146 | break;
147 | case "FORT_DETAILS":
148 | this.FortDetails(msg).then((result) => {
149 | resolve(result);
150 | });
151 | break;
152 | case "GET_MAP_OBJECTS":
153 | this.GetMapObjects(msg).then((result) => {
154 | resolve(result);
155 | });
156 | break;
157 | case "CHECK_CHALLENGE":
158 | resolve(this.CheckChallenge(msg));
159 | break;
160 | case "GET_DOWNLOAD_URLS":
161 | resolve(this.GetDownloadUrls(msg));
162 | break;
163 | case "DOWNLOAD_SETTINGS":
164 | resolve(this.DownloadSettings(msg));
165 | break;
166 | case "DOWNLOAD_REMOTE_CONFIG_VERSION":
167 | resolve(this.DownloadRemoteConfigVersion(msg));
168 | break;
169 | case "DOWNLOAD_ITEM_TEMPLATES":
170 | resolve(this.DownloadItemTemplates(msg));
171 | break;
172 | };
173 | });
174 | }
175 |
176 | }
177 |
178 | inherit(World, _forts);
179 | inherit(World, _players);
180 | inherit(World, _packets);
--------------------------------------------------------------------------------
/src/models/World/packets/CatchPokemon.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | */
6 | export default function CatchPokemon(msg) {
7 |
8 | let buffer = null;
9 | let schema = "POGOProtos.Networking.Responses.CatchPokemonResponse";
10 |
11 | let player = msg.player;
12 | let bag = player.bag;
13 | let ball = bag.getLocalItemKey(msg.pokeball);
14 |
15 | let pkmn = msg.player.currentEncounter;
16 |
17 | player.bag[ball] -= 1;
18 |
19 | return new Promise((resolve) => {
20 | // Invalid pkmn
21 | if (!pkmn) {
22 | player.currentEncounter = null;
23 | pkmn.caughtBy(player);
24 | buffer = {
25 | status: "CATCH_ERROR"
26 | };
27 | // Missed
28 | } else if (!msg.hit_pokemon || !bag[ball]) {
29 | buffer = {
30 | status: "CATCH_MISSED"
31 | };
32 | } else {
33 | // Fleed
34 | if (Math.random() < .1) {
35 | pkmn.caughtBy(player);
36 | player.currentEncounter = null;
37 | buffer = {
38 | status: "CATCH_FLEE"
39 | };
40 | // Escaped
41 | } else if (Math.random() < .2) {
42 | buffer = {
43 | status: "CATCH_ESCAPE"
44 | };
45 | // Catched?
46 | } else {
47 | player.catchPkmn(pkmn, msg.pokeball).then((result) => {
48 | resolve(POGOProtos.serialize(result, schema));
49 | });
50 | return void 0;
51 | }
52 | }
53 | resolve(POGOProtos.serialize(buffer, schema));
54 | });
55 |
56 | }
--------------------------------------------------------------------------------
/src/models/World/packets/CheckChallenge.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | */
6 | export default function CheckChallenge(msg) {
7 |
8 | let buffer = {
9 | challenge_url: " "
10 | };
11 |
12 | return (
13 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CheckChallengeResponse")
14 | );
15 |
16 | }
--------------------------------------------------------------------------------
/src/models/World/packets/DownloadItemTemplates.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import * as shared from "../../../shared";
4 |
5 | /**
6 | * @param {Object} msg
7 | * @return {Buffer}
8 | */
9 | export default function DownloadItemTemplates(msg) {
10 | return (
11 | shared.GAME_MASTER.serialize()
12 | );
13 | }
--------------------------------------------------------------------------------
/src/models/World/packets/DownloadRemoteConfigVersion.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | */
6 | export default function DownloadRemoteConfigVersion(msg) {
7 |
8 | let buffer = {
9 | "result": "SUCCESS",
10 | "item_templates_timestamp_ms": "1471650700946",
11 | "asset_digest_timestamp_ms": "1467338276561000",
12 | "$unknownFields": []
13 | }
14 |
15 | return (
16 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.DownloadRemoteConfigVersionResponse")
17 | );
18 |
19 | }
--------------------------------------------------------------------------------
/src/models/World/packets/DownloadSettings.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import Settings from "../../../modes";
4 |
5 | /**
6 | * @param {Object} msg
7 | * @return {Buffer}
8 | */
9 | export default function DownloadSettings(msg) {
10 |
11 | let buffer = {
12 | hash: "2788184af4004004d6ab0740f7632983332106f6",
13 | settings: Settings.GAME_SETTINGS
14 | };
15 |
16 | return (
17 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.DownloadSettingsResponse")
18 | );
19 |
20 | }
--------------------------------------------------------------------------------
/src/models/World/packets/Encounter.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | /**
4 | * @param {Object} msg
5 | */
6 | export default function Encounter(msg) {
7 |
8 | let player = msg.player;
9 |
10 | // Try to use cached encounter
11 | let encounter = player.currentEncounter;
12 |
13 | // Dont use cached encounter
14 | if (
15 | encounter === null ||
16 | encounter.uid !== parseInt(msg.encounter_id)
17 | ) {
18 | encounter = this.getEncounterById(msg.encounter_id);
19 | }
20 |
21 | let buffer = {
22 | status: "ENCOUNTER_SUCCESS",
23 | capture_probability: {
24 | pokeball_type: ["ITEM_POKE_BALL", "ITEM_GREAT_BALL", "ITEM_ULTRA_BALL"],
25 | capture_probability: [1, 1, 1]
26 | }
27 | };
28 |
29 | // Invalid pkmn
30 | if (!encounter) {
31 | player.currentEncounter = null;
32 | buffer.status = "ENCOUNTER_NOT_FOUND";
33 | }
34 | // Already encountered
35 | else if (encounter.alreadyCatchedBy(player)) {
36 | buffer.status = "ENCOUNTER_ALREADY_HAPPENED";
37 | }
38 | // Encounter success
39 | else {
40 | player.currentEncounter = encounter;
41 | encounter.seenBy(player);
42 | buffer.wild_pokemon = encounter.serializeWild();
43 | buffer.wild_pokemon.pokemon_data.cp = encounter.getSeenCp(player);
44 | }
45 |
46 | return (
47 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.EncounterResponse")
48 | );
49 |
50 | }
--------------------------------------------------------------------------------
/src/models/World/packets/FortDetails.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import CFG from "../../../../cfg";
4 | import print from "../../../print";
5 |
6 | /**
7 | * @param {Object} msg
8 | * @return {Buffer}
9 | */
10 | export default function FortDetails(msg) {
11 |
12 | return new Promise((resolve) => {
13 | this.getFortDataById(msg.fort_id).then((fort) => {
14 | if (!fort) return void 0;
15 | let url = fort.image_url;
16 | let buffer = {
17 | fort_id: msg.fort_id,
18 | name: fort.name,
19 | description: fort.description,
20 | image_urls: [
21 | url
22 | ],
23 | type: "CHECKPOINT",
24 | latitude: fort.latitude,
25 | longitude: fort.longitude,
26 | modifiers: []
27 | };
28 | resolve(
29 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.FortDetailsResponse")
30 | );
31 | });
32 | });
33 |
34 | }
--------------------------------------------------------------------------------
/src/models/World/packets/FortSearch.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import CFG from "../../../../cfg";
4 | import print from "../../../print";
5 |
6 | /**
7 | * @param {Object} msg
8 | * @return {Buffer}
9 | */
10 | export default function FortSearch(msg) {
11 |
12 | let player = msg.player;
13 |
14 | return new Promise((resolve) => {
15 | this.getFortDataById(msg.fort_id).then((fort) => {
16 | if (!fort) return void 0;
17 | player.consumeFortRewards(fort);
18 | // TODO: disable rewarding when on cooldown
19 | let buffer = ({
20 | result: "SUCCESS",
21 | items_awarded: fort.serializeRewards(),
22 | experience_awarded: fort.experience,
23 | cooldown_complete_timestamp_ms: +new Date() + fort.cooldown,
24 | chain_hack_sequence_number: 2
25 | });
26 | player.info.upgradeExp(fort.experience);
27 | resolve(
28 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.FortSearchResponse")
29 | );
30 | });
31 | });
32 |
33 | }
--------------------------------------------------------------------------------
/src/models/World/packets/GetDownloadUrls.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import CFG from "../../../../cfg";
4 | import print from "../../../print";
5 |
6 | import { GAME_ASSETS } from "../../../shared";
7 |
8 | /**
9 | * @param {Object} msg
10 | * @return {Buffer}
11 | */
12 | export default function GetDownloadUrls(msg) {
13 |
14 | let asset = null;
15 | let assets = GAME_ASSETS[msg.player.platform].decode.digest;
16 | let assetId = msg.asset_id[0];
17 |
18 | let ii = 0;
19 | let length = assets.length;
20 |
21 | for (; ii < length; ++ii) {
22 | if ((asset = assets[ii]).asset_id === assetId) {
23 | break;
24 | }
25 | };
26 |
27 | let buffer = {
28 | download_urls: [{
29 | asset_id: assetId,
30 | url: `http://${CFG.LOCAL_IP || this.instance.getLocalIPv4()}:${CFG.PORT}/model/${asset.bundle_name}`,
31 | size: asset.size,
32 | checksum: asset.checksum
33 | }]
34 | };
35 |
36 | return (
37 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetDownloadUrlsResponse")
38 | );
39 |
40 | }
--------------------------------------------------------------------------------
/src/models/World/packets/GetMapObjects.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import CFG from "../../../../cfg";
4 | import print from "../../../print";
5 |
6 | /**
7 | * @param {Object} msg
8 | * @return {Buffer}
9 | */
10 | export default function GetMapObjects(msg) {
11 |
12 | let latitude = msg.latitude;
13 | let longitude = msg.longitude;
14 |
15 | let mapCells = [];
16 |
17 | let buffer = {
18 | status: "SUCCESS",
19 | map_cells: mapCells
20 | };
21 |
22 | return new Promise((resolve) => {
23 | this.getFortsByCells(msg.cell_id, [], 0).then((cells) => {
24 | cells.map((cell) => {
25 | if (cell.forts.length) {
26 | let ids = [];
27 | cell.forts.map((fort) => {
28 | let id = fort.cellId + "." + fort.uid;
29 | if (ids.indexOf(id) > -1) print(`Duplicated fort!!!! => ${id}`, 31);
30 | else ids.push(id);
31 | });
32 | }
33 | mapCells.push(cell.serialize(msg.player));
34 | });
35 | resolve(
36 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse")
37 | );
38 | });
39 | });
40 |
41 | }
--------------------------------------------------------------------------------
/src/models/World/packets/index.js:
--------------------------------------------------------------------------------
1 | export Encounter from "./Encounter";
2 | export FortSearch from "./FortSearch";
3 | export FortDetails from "./FortDetails";
4 | export CatchPokemon from "./CatchPokemon";
5 | export GetMapObjects from "./GetMapObjects";
6 | export CheckChallenge from "./CheckChallenge";
7 | export GetDownloadUrls from "./GetDownloadUrls";
8 | export DownloadSettings from "./DownloadSettings";
9 | export DownloadItemTemplates from "./DownloadItemTemplates";
10 | export DownloadRemoteConfigVersion from "./DownloadRemoteConfigVersion";
--------------------------------------------------------------------------------
/src/models/World/players.js:
--------------------------------------------------------------------------------
1 | import Player from "../Player";
2 |
3 | import CFG from "../../../cfg";
4 |
5 | import print from "../../print";
6 |
7 | /**
8 | * @return {Boolean}
9 | */
10 | export function isFull() {
11 | return (
12 | this.connectedPlayers >= CFG.MAX_CONNECTIONS
13 | );
14 | }
15 |
16 | /**
17 | * @param {Request} req
18 | * @param {Response} res
19 | */
20 | export function getPlayerByRequest(req, res) {
21 |
22 | let player = null;
23 |
24 | if (this.playerAlreadyConnected(req)) {
25 | player = this.getPlayerByIP(req.headers.host);
26 | return (player);
27 | }
28 | else {
29 | player = this.addPlayer(req, res);
30 | return (player);
31 | }
32 |
33 | }
34 |
35 | /**
36 | * @param {Request} req
37 | * @return {Boolean}
38 | */
39 | export function playerAlreadyConnected(req) {
40 |
41 | let ii = 0;
42 | let length = this.connectedPlayers;
43 |
44 | let remoteAddress = req.headers.host;
45 |
46 | for (; ii < length; ++ii) {
47 | if (this.players[ii].remoteAddress === remoteAddress) {
48 | return (true);
49 | }
50 | };
51 |
52 | return (false);
53 |
54 | }
55 |
56 | /**
57 | * @param {String} ip
58 | */
59 | export function getPlayerByIP(ip) {
60 |
61 | let players = this.players;
62 |
63 | let ii = 0;
64 | let length = this.connectedPlayers;
65 |
66 | for (; ii < length; ++ii) {
67 | if (players[ii].remoteAddress === ip) {
68 | return (players[ii]);
69 | }
70 | };
71 |
72 | return (null);
73 |
74 | }
75 |
76 | /**
77 | * @param {String} name
78 | */
79 | export function getPlayerByName(name) {
80 |
81 | let players = this.players;
82 |
83 | let ii = 0;
84 | let length = this.connectedPlayers;
85 |
86 | for (; ii < length; ++ii) {
87 | if (players[ii].username === name) {
88 | return (players[ii]);
89 | }
90 | };
91 |
92 | return (null);
93 |
94 | }
95 |
96 | /**
97 | * @param {String} name
98 | * @return {Boolean}
99 | */
100 | export function validPlayerName(name) {
101 | return !(
102 | name === null ||
103 | name === void 0 ||
104 | typeof name === "string" &&
105 | name.length <= 3 ||
106 | name.length > 16
107 | );
108 | }
109 |
110 | /**
111 | * @param {String} email
112 | */
113 | export function playerIsRegistered(email) {
114 | return new Promise((resolve) => {
115 | this.db.query(`SELECT * FROM ${CFG.MYSQL_USERS_TABLE} WHERE email=?`, [email], (e, rows) => {
116 | if (e) return print(e, 31);
117 | resolve(rows.length >= 1);
118 | });
119 | });
120 | }
121 |
122 | /**
123 | * @param {Player} player
124 | */
125 | export function registerPlayer(player) {
126 | return new Promise((resolve) => {
127 | this.db.query(`INSERT INTO ${CFG.MYSQL_USERS_TABLE} SET email=?, username=? `, [player.email, player.username], (e, res) => {
128 | if (e) return print(e, 31);
129 | resolve();
130 | });
131 | });
132 | }
133 |
134 | /**
135 | * @param {Request} req
136 | * @param {Response} res
137 | * @return {Player}
138 | */
139 | export function addPlayer(req, res) {
140 |
141 | let player = new Player({
142 | world: this,
143 | request: req,
144 | response: res
145 | });
146 |
147 | player.remoteAddress = req.headers.host;
148 |
149 | this.players.push(player);
150 |
151 | return (player);
152 |
153 | }
154 |
155 | /**
156 | * @param {Player} player
157 | */
158 | export function removePlayer(player) {
159 | print(`Removed ${player.email}`, 36);
160 | }
161 |
162 | /**
163 | * @param {String} name
164 | */
165 | export function kickPlayer(name) {
166 |
167 | if (!this.validPlayerName(name)) {
168 | print(`Invalid player name!`, 31);
169 | return void 0;
170 | }
171 |
172 | let player = this.getPlayerByName(name);
173 |
174 | if (player !== null) {
175 | this.removePlayer(player);
176 | print(`Kicked ${name} from the server!`);
177 | }
178 | else print(`Failed to kick ${name} from the server!`, 31);
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/src/modes/index.js:
--------------------------------------------------------------------------------
1 | import CFG from "../../cfg";
2 |
3 | export default {
4 | GAME_SETTINGS: {
5 | fort_settings: {
6 | interaction_range_meters: 40.25098039215686,
7 | max_total_deployed_pokemon: 10,
8 | max_player_deployed_pokemon: 1,
9 | deploy_stamina_multiplier: 8.062745098039215,
10 | deploy_attack_multiplier: 0,
11 | far_interaction_range_meters: 1000.0156862745098
12 | },
13 | map_settings: {
14 | pokemon_visible_range: 999.00196078431372,
15 | poke_nav_range_meters: 751.0156862745098,
16 | encounter_range_meters: 999.25098039215686,
17 | get_map_objects_min_refresh_seconds: 16,
18 | get_map_objects_max_refresh_seconds: 16,
19 | get_map_objects_min_distance_meters: 10.007843017578125,
20 | google_maps_api_key: CFG.GMAPS_KEY
21 | },
22 | inventory_settings: {
23 | max_pokemon: 1000,
24 | max_bag_items: 1000,
25 | base_pokemon: 250,
26 | base_bag_items: 350,
27 | base_eggs: 9
28 | },
29 | minimum_client_version: CFG.MINIMUM_CLIENT_VERSION
30 | },
31 | PKMN_SETTINGS: {
32 | MIN_IV: 1,
33 | MAX_IV: 15
34 | }
35 | }
--------------------------------------------------------------------------------
/src/print.js:
--------------------------------------------------------------------------------
1 | import CFG from "../cfg";
2 |
3 | /**
4 | * @param {String} msg
5 | * @param {Number} color
6 | * @param {Boolean} nl
7 | */
8 | export default function print(msg, color, newline) {
9 | color = Number.isInteger(color) ? color : CFG.DEFAULT_CONSOLE_COLOR;
10 | process.stdout.write(`[Console] \x1b[${color};1m${msg}\x1b[0m${newline === void 0 ? "\n" : ""}`);
11 | }
--------------------------------------------------------------------------------
/src/process.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 |
3 | import print from "./print";
4 | import CFG from "../cfg";
5 |
6 | const helpMessage = fs.readFileSync(".help", "utf8");
7 |
8 | export function processCommand(cmd, data) {
9 | let players = this.world.players;
10 | switch (cmd) {
11 | // How many active connections there are
12 | case "/players":
13 | var length = players.length;
14 | print(`${length}/${CFG.MAX_CONNECTIONS} connected players!`, 33);
15 | break;
16 | // Exit the server
17 | case "/exit":
18 | this.shutdown();
19 | break;
20 | case "/kick":
21 | this.world.kickPlayer(data[0]);
22 | break;
23 | case "/kickall":
24 | var length = players.length;
25 | this.removeAllPlayers();
26 | var result = length - players.length;
27 | print(`Removed ${result} player${result === 1 ? "": "s"}!`);
28 | break;
29 | case "/clear":
30 | process.stdout.write("\x1Bc");
31 | this.greet();
32 | break;
33 | case "/help":
34 | console.log("\x1b[36;1m==================================== HELP =====================================\x1b[0m");
35 | console.log(`\x1b[${CFG.DEFAULT_CONSOLE_COLOR};1m${helpMessage}\x1b[0m`);
36 | console.log("\x1b[36;1m===============================================================================\x1b[0m");
37 | break;
38 | case "/save":
39 | this.saveAllPlayers();
40 | var length = players.length;
41 | print(`Saved ${length} player${length === 1 ? "": "s"} into database!`);
42 | break;
43 | case "/spawn":
44 | this.spawnPkmnAtPlayer(data[0], data[1], data[2] || 1);
45 | break;
46 | case "/dump":
47 | print("Preparing dump session..");
48 | this.onFirstRun(() => {
49 | print("Dumped assets successfully!");
50 | });
51 | break;
52 | default:
53 | print(`${cmd} is not a valid command!`, 31);
54 | break;
55 | };
56 | };
57 |
58 | export function stdinInput(data) {
59 | if (data.length <= 1) return void 0;
60 | let args = data.split(" ");
61 | let cmds = args.shift()
62 | this.processCommand(cmds, args);
63 | };
64 |
65 | export function uncaughtException(e) {
66 | switch (e.errno) {
67 | case "EADDRINUSE":
68 | print(`Port ${CFG.PORT} is already in use!`, 31);
69 | break;
70 | case "EACCES":
71 | print("No root privileges!", 31);
72 | break;
73 | default:
74 | print("Unhandled exception occurred: ", 31);
75 | print(e, 31);
76 | print(e.stack, 31);
77 | break;
78 | };
79 | print("The server has crashed!", 31);
80 | };
--------------------------------------------------------------------------------
/src/request.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import url from "url";
3 | import jwtDecode from "jwt-decode";
4 | import POGOProtos from "pokemongo-protobuf";
5 |
6 | import print from "./print";
7 | import CFG from "../cfg";
8 |
9 | import {
10 | deXOR,
11 | validEmail,
12 | getHashCodeFrom
13 | } from "./utils";
14 |
15 | /**
16 | * @param {Request} req
17 | * @param {Response} res
18 | */
19 | export function routeRequest(req, res) {
20 |
21 | let parsed = url.parse(req.url).pathname;
22 | let route = parsed.split("/");
23 | let host = req.headers.host;
24 |
25 | switch (route[1]) {
26 | case "plfe":
27 | case "custom":
28 | this.processRpcRequest(req, res, route);
29 | break;
30 | case "model":
31 | this.processModelRequest(req, res, route);
32 | break;
33 | case "api":
34 | if (!CFG.API_ENABLE) {
35 | print(`API is disabled! Denied API access for ${host}!`, 31);
36 | return void 0;
37 | }
38 | if (req.method === "POST") {
39 | this.processApiCall(req, res, route);
40 | }
41 | break;
42 | default:
43 | print(`Unknown request url: https://${host}${req.url}`, 31);
44 | break;
45 | };
46 |
47 | }
48 |
49 | /**
50 | * @param {Request} req
51 | * @param {Response} res
52 | * @param {Array} route
53 | */
54 | export function processModelRequest(req, res, route) {
55 |
56 | let player = this.world.getPlayerByRequest(req, res);
57 |
58 | // make sure no random dudes can access download
59 | if (!player.authenticated) return void 0;
60 | let name = route[2];
61 | if (name && name.length > 1) {
62 | let folder = player.isAndroid ? "android/" : "ios/";
63 | fs.readFile("data/" + folder + name, (error, data) => {
64 | if (error) {
65 | print(`Error file resolving model ${name}:` + error, 31);
66 | return void 0;
67 | }
68 | print(`Sent ${name} to ${player.email}`, 36);
69 | res.end(data);
70 | });
71 | }
72 |
73 | }
74 |
75 | /**
76 | * @param {Request} req
77 | * @param {Response} res
78 | * @param {Array} route
79 | */
80 | export function processRpcRequest(req, res, route) {
81 | let player = this.world.getPlayerByRequest(req, res);
82 | if (route[2] === "rpc") {
83 | player.refreshSocket(req, res);
84 | this.onRequest(player);
85 | }
86 | }
87 |
88 | /**
89 | * @param {Player} player
90 | */
91 | export function onRequest(player) {
92 |
93 | let request = player.request;
94 | request.requests = request.requests || [];
95 |
96 | if (!player.authenticated) {
97 | this.authenticatePlayer(player);
98 | return void 0;
99 | }
100 |
101 | if (player.hasSignature === false) {
102 | player.getDevicePlatform();
103 | }
104 |
105 | if (!request.requests.length) {
106 | // Dirty hack, appears when open pkmn stats in inventory
107 | if (request.unknown6 && request.unknown6[1].request_type === 6) {
108 | let msg = this.envelopResponse([], request, player);
109 | player.sendResponse(msg);
110 | }
111 | else {
112 | print("Received invalid request!", 31);
113 | return void 0;
114 | }
115 | }
116 |
117 | this.processRequests(player, request.requests).then((returns) => {
118 | if (CFG.DEBUG_DUMP_TRAFFIC) {
119 | this.dumpTraffic(request, returns);
120 | }
121 | let msg = this.envelopResponse(returns, request, player);
122 | if (CFG.DEBUG_LOG_REQUESTS) {
123 | print(`##### ${this.getCurrentTime()}`);
124 | let index = 0;
125 | request.requests.map((request) => {
126 | let reqSize = Buffer.byteLength(request.request_message, "utf8");
127 | let resSize = Buffer.byteLength(returns[index], "utf8");
128 | print(`${request.request_type} ${reqSize} => ${resSize}`, 37);
129 | index++;
130 | });
131 | }
132 | player.sendResponse(msg);
133 | });
134 |
135 | }
136 |
137 | /**
138 | * @param {Array} returns
139 | * @param {Request} request
140 | * @param {Player} player
141 | * @return {Buffer}
142 | */
143 | export function envelopResponse(returns, request, player) {
144 |
145 | let buffer = request;
146 |
147 | buffer.returns = returns;
148 |
149 | if (request.auth_ticket) {
150 | print("Authenticate!", 31);
151 | buffer.auth_ticket = AuthTicket();
152 | }
153 |
154 | if (buffer.unknown6) {
155 | buffer.unknown6 = [{
156 | "response_type": 6,
157 | "unknown2": {
158 | "unknown1": "1",
159 | "items": [],
160 | "player_currencies": []
161 | }
162 | }];
163 | }
164 |
165 | buffer.status_code = 1;
166 |
167 | return (
168 | POGOProtos.serialize(buffer, "POGOProtos.Networking.Envelopes.ResponseEnvelope")
169 | );
170 |
171 | }
172 |
173 | /**
174 | * @param {Player} player
175 | */
176 | export function authenticatePlayer(player) {
177 |
178 | let request = player.request;
179 | let token = request.auth_info;
180 | let msg = player.GetAuthTicket(request.request_id);
181 |
182 | if (!token || !token.provider) {
183 | print("Invalid authentication token! Kicking..", 31);
184 | player.world.removePlayer(player);
185 | return void 0;
186 | }
187 |
188 | if (token.provider === "google") {
189 | if (token.token !== null) {
190 | let decoded = jwtDecode(token.token.contents);
191 | player.email = decoded.email;
192 | player.email_verified = decoded.email_verified;
193 | player.isGoogleAccount = true;
194 | print(`${player.email} connected!`, 36);
195 | }
196 | else {
197 | print("Invalid authentication token! Kicking..", 31);
198 | player.world.removePlayer(player);
199 | return void 0;
200 | }
201 | }
202 | else {
203 | print("Invalid provider! Kicking..", 31);
204 | player.world.removePlayer(player);
205 | return void 0;
206 | }
207 |
208 | if (!validEmail(player.email)) return void 0;
209 |
210 | player.authenticated = true;
211 |
212 | this.world.playerIsRegistered(player.email).then((truth) => {
213 | // Register
214 | if (!truth) {
215 | this.world.registerPlayer(player).then((id) => {
216 | player.syncWithDatabase().then(() => {
217 | player.sendResponse(msg);
218 | });
219 | });
220 | }
221 | // Login
222 | else {
223 | player.syncWithDatabase().then(() => {
224 | player.sendResponse(msg);
225 | });
226 | }
227 | });
228 |
229 | }
230 |
231 | /**
232 | * @param {Player} player
233 | * @param {Array} requests
234 | * @return {Array}
235 | */
236 | export function processRequests(player, requests) {
237 |
238 | return new Promise((resolve) => {
239 |
240 | let index = 0;
241 | let length = requests.length;
242 | let body = [];
243 |
244 | const loop = (index) => {
245 | this.processResponse(player, requests[index]).then((request) => {
246 | body.push(request);
247 | if (++index >= length) resolve(body);
248 | else return loop(index);
249 | });
250 | };
251 |
252 | loop(0);
253 |
254 | });
255 |
256 | }
--------------------------------------------------------------------------------
/src/response.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 |
3 | import print from "./print";
4 | import CFG from "../cfg";
5 |
6 | import {
7 | _toCC
8 | } from "./utils";
9 |
10 | /**
11 | * @param {Request} req
12 | * @param {String} type
13 | * @return {Buffer}
14 | */
15 | export function parseMessage(req, type) {
16 | let proto = `POGOProtos.Networking.Requests.Messages.${type}Message`;
17 | if (req.request_message) {
18 | try {
19 | return (this.parseProtobuf(req.request_message, proto));
20 | } catch (e) {
21 | print(`Failed to parse ${type}: ${e}`, 31);
22 | }
23 | }
24 | return void 0;
25 | }
26 |
27 | /**
28 | * @param {Player} player
29 | * @param {Request} req
30 | * @return {Buffer}
31 | */
32 | export function processResponse(player, req) {
33 |
34 | let cc = _toCC(req.request_type);
35 | let msg = this.parseMessage(req, cc) || {};
36 |
37 | return new Promise((resolve) => {
38 |
39 | try {
40 | switch (req.request_type) {
41 | // Player
42 | case "SET_AVATAR":
43 | case "GET_PLAYER":
44 | case "GET_INVENTORY":
45 | case "RELEASE_POKEMON":
46 | case "UPGRADE_POKEMON":
47 | case "GET_ASSET_DIGEST":
48 | case "NICKNAME_POKEMON":
49 | case "CLAIM_CODENAME":
50 | case "GET_HATCHED_EGGS":
51 | case "LEVEL_UP_REWARDS":
52 | case "GET_PLAYER_PROFILE":
53 | case "CHECK_AWARDED_BADGES":
54 | case "SET_FAVORITE_POKEMON":
55 | case "RECYCLE_INVENTORY_ITEM":
56 | player.getPacket(req.request_type, msg).then((result) => {
57 | resolve(result);
58 | });
59 | return void 0;
60 | break;
61 | // Global
62 | case "ENCOUNTER":
63 | case "FORT_SEARCH":
64 | case "FORT_DETAILS":
65 | case "CATCH_POKEMON":
66 | case "GET_MAP_OBJECTS":
67 | case "CHECK_CHALLENGE":
68 | case "GET_DOWNLOAD_URLS":
69 | case "DOWNLOAD_SETTINGS":
70 | case "DOWNLOAD_REMOTE_CONFIG_VERSION":
71 | case "DOWNLOAD_ITEM_TEMPLATES":
72 | case "MARK_TUTORIAL_COMPLETE":
73 | msg.player = player;
74 | player.world.getPacket(req.request_type, msg).then((result) => {
75 | resolve(result);
76 | });
77 | return void 0;
78 | break;
79 | default:
80 | print(`Unknown request: ${req.request_type}`, 31);
81 | break;
82 | };
83 | } catch (e) {
84 | print(`Response error: ${e}`, 31);
85 | };
86 |
87 | });
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/setup.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import fse from "fs-extra";
3 | import pogo from "pogo-asset-downloader";
4 | import POGOProtos from "pokemongo-protobuf";
5 |
6 | import print from "./print";
7 | import CFG from "../cfg";
8 |
9 | import * as shared from "./shared";
10 |
11 | import GameMaster from "./models/GameMaster";
12 |
13 | import {
14 | _toCC,
15 | capitalize,
16 | idToPkmnBundleName
17 | } from "./utils";
18 |
19 | export function setup() {
20 |
21 | return new Promise((resolve, reject) => {
22 |
23 | // make sure all assets got loaded properly
24 | this.validateAssets().then(() => {
25 |
26 | print(`Downloaded assets are valid! Proceeding..`);
27 |
28 | shared.GAME_MASTER = new GameMaster(this);
29 |
30 | this.setupDatabaseConnection().then(() => {
31 | if (CFG.PORT < 1) {
32 | print("Invalid port!", 31);
33 | return void 0;
34 | }
35 | this.socket = this.createHTTPServer();
36 | setTimeout(this::this.cycle, 1);
37 | let localIPv4 = this.getLocalIPv4();
38 | print(`Server listening at ${localIPv4}:${CFG.PORT}`, 33);
39 | resolve();
40 | });
41 |
42 | }).catch((e) => {
43 | print("Error: " + e + " was not found!", 31);
44 | this.initDumpSession().then(resolve);
45 | });
46 |
47 | });
48 |
49 | }
50 |
51 | export function initDumpSession() {
52 | print("Required assets are missing! Preparing dump session..", 33);
53 | return new Promise((resolve, reject) => {
54 | setTimeout(() => {
55 | this.onFirstRun(() => {
56 | this.setup().then(resolve);
57 | });
58 | }, 1e3);
59 | });
60 | }
61 |
62 | /**
63 | * Make sure all required
64 | * assets got loaded properly
65 | */
66 | export function validateAssets() {
67 |
68 | return new Promise((resolve, reject) => {
69 |
70 | // validate game master
71 | if (!this.fileExists(CFG.DUMP_ASSET_PATH + "game_master")) {
72 | return reject("File game_master");
73 | }
74 |
75 | this.validateModels().then(() => {
76 | resolve();
77 | }).catch((e) => {
78 | reject(e);
79 | });
80 |
81 | });
82 |
83 | }
84 |
85 | export function validateModels() {
86 |
87 | let max = CFG.MAX_POKEMON_NATIONAL_ID;
88 | let limit = pogo.platforms.length;
89 |
90 | return new Promise((resolve, reject) => {
91 | const validate = (index) => {
92 | let platform = pogo.platforms[index].name;
93 | let path = CFG.DUMP_ASSET_PATH + platform + "/";
94 |
95 | // ups, validate asset_digest's too
96 | if (!this.fileExists(path + "asset_digest")) {
97 | return reject(`${path}asset_digest`);
98 | }
99 | else {
100 | let buffer = fs.readFileSync(path + "asset_digest");
101 | shared.GAME_ASSETS[platform] = {
102 | buffer: buffer,
103 | decode: this.parseProtobuf(buffer, "POGOProtos.Networking.Responses.GetAssetDigestResponse")
104 | };
105 | }
106 |
107 | // validate models inside folder
108 | let ii = 0;
109 | while (++ii <= max) {
110 | let id = idToPkmnBundleName(ii);
111 | if (!this.fileExists(path + id)) {
112 | return reject("Model " + id);
113 | }
114 | };
115 |
116 | if (++index >= limit) {
117 | resolve();
118 | return void 0;
119 | }
120 | validate(index);
121 | };
122 |
123 | validate(0);
124 | });
125 |
126 | }
127 |
128 | export function onFirstRun(resolve) {
129 | print(`Attempt to login with ${_toCC(CFG.DOWNLOAD_PROVIDER)}..`, 33);
130 | pogo.login({
131 | provider: CFG.DOWNLOAD_PROVIDER, // google or ptc
132 | username: CFG.DOWNLOAD_USERNAME,
133 | password: CFG.DOWNLOAD_PASSWORD
134 | }).then(() => {
135 | print(`Successfully logged in!`);
136 | this.downloadAssetDigests().then(() => {
137 | this.downloadAssets().then(resolve);
138 | });
139 | }).catch((e) => {
140 | print(e, 31);
141 | });
142 | }
143 |
144 | export function downloadAssetDigests(assets) {
145 | return new Promise((resolve, reject) => {
146 | // create data folder for each support platform
147 | // and download each asset digest and related models
148 | let index = 0;
149 | let length = pogo.platforms.length;
150 | for (let platform of pogo.platforms) {
151 | fse.ensureDirSync(CFG.DUMP_ASSET_PATH + platform.name);
152 | pogo.getAssetDigest(platform).then((asset) => {
153 | fs.writeFileSync(CFG.DUMP_ASSET_PATH + platform.name + "/asset_digest", asset.toBuffer());
154 | if (++index >= length) resolve();
155 | });
156 | };
157 | });
158 | }
159 |
160 | export function downloadAssets() {
161 | return new Promise((resolve, reject) => {
162 | pogo.getGameMaster().then((master) => {
163 | fs.writeFileSync(CFG.DUMP_ASSET_PATH + "game_master", master.toBuffer());
164 | this.downloadModels().then(() => {
165 | resolve();
166 | });
167 | });
168 | });
169 | }
170 |
171 | export function downloadModels() {
172 |
173 | let limit = pogo.platforms.length;
174 |
175 | return new Promise((resolve, reject) => {
176 | const dump = (index) => {
177 | let platform = pogo.platforms[index];
178 | let name = platform.name;
179 | let caps = capitalize(name);
180 | caps = name === "ios" ? "iOS" : caps;
181 | pogo.setPlatform(name);
182 | print(`Preparing to dump ${caps} assets..`, 36);
183 | this.dumpPkmnModels(CFG.DUMP_ASSET_PATH + name + "/", () => {
184 | print(`Dumped ${CFG.MAX_POKEMON_NATIONAL_ID} ${caps} assets successfully!`);
185 | if (++index >= limit) {
186 | print("Dumped all assets successfully!");
187 | resolve();
188 | return void 0;
189 | }
190 | dump(index);
191 | });
192 | };
193 | dump(0);
194 | });
195 |
196 | }
197 |
198 | export function dumpPkmnModels(path, resolve) {
199 |
200 | let limit = CFG.MAX_POKEMON_NATIONAL_ID;
201 |
202 | const dump = (index) => {
203 | let ids = [];
204 | if (++index <= limit) ids.push(index);
205 | if (++index <= limit) ids.push(index);
206 | if (++index <= limit) ids.push(index);
207 | pogo.getAssetByPokemonId(ids).then((downloads) => {
208 | downloads.map((item) => {
209 | print(`Dumping model ${item.name}..`, 35);
210 | try {
211 | fs.writeFileSync(path + item.name, item.body);
212 | }
213 | catch (e) {
214 | print(`Error while dumping model ${item.name}:` + e, 31);
215 | }
216 | });
217 | if (index >= limit) {
218 | //print(`Dumped ${limit} assets successfully!`);
219 | resolve();
220 | return void 0;
221 | }
222 | setTimeout(() => dump(index), 1e3);
223 | });
224 | };
225 |
226 | dump(0);
227 |
228 | }
--------------------------------------------------------------------------------
/src/shared.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Global shared assets
3 | */
4 |
5 | export let GAME_ASSETS = {};
6 | export let GAME_MASTER = null;
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import POGOProtos from "pokemongo-protobuf";
2 | import pcrypt from "pcrypt";
3 |
4 | import CFG from "../cfg";
5 |
6 | /**
7 | * @param {Object} cls
8 | * @param {Object} prot
9 | * @export
10 | */
11 | export function inherit(cls, prot) {
12 |
13 | let key = null;
14 |
15 | for (key in prot) {
16 | if (prot[key] instanceof Function) {
17 | cls.prototype[key] = prot[key];
18 | }
19 | };
20 |
21 | }
22 |
23 | let hashIndex = 1;
24 |
25 | /**
26 | * @return {Number}
27 | */
28 | export function getUniqueHash() {
29 | if (++hashIndex >= Number.MAX_SAFE_INTEGER) {
30 | hashIndex = 0;
31 | }
32 | return (hashIndex);
33 | }
34 |
35 | /**
36 | * http://stackoverflow.com/a/7616484/3367904
37 | * @param {String} str
38 | * @return {String}
39 | */
40 | export function getHashCodeFrom(str) {
41 | var hash = 0, i, chr, len;
42 | if (str.length === 0) return hash;
43 | for (i = 0, len = str.length; i < len; i++) {
44 | chr = str.charCodeAt(i);
45 | hash = ((hash << 5) - hash) + chr;
46 | hash |= 0; // Convert to 32bit integer
47 | }
48 | return hash;
49 | }
50 |
51 | export function deXOR(value, hash) {
52 | let out = "";
53 | let ii = 0;
54 | let length = value.length;
55 | for (; ii < length; ++ii) {
56 | out += String.fromCharCode(hash ^ value.charCodeAt(ii));
57 | };
58 | return (out);
59 | }
60 |
61 | /**
62 | * @return {Number}
63 | */
64 | export function randomRequestId() {
65 | return (
66 | 1e18 - Math.floor(Math.random() * 1e18)
67 | );
68 | }
69 |
70 | /**
71 | * @param {String} key
72 | * @return {String}
73 | */
74 | export function _toCC(key) {
75 | key = key.toLowerCase();
76 | let res = key[0].toUpperCase() + key.substring(1, key.length).replace(/_\s*([a-z])/g, function(d, e) {
77 | return e.toUpperCase();
78 | });
79 | return (res);
80 | }
81 |
82 | /**
83 | * @param {Number} index
84 | * @return {String}
85 | */
86 | export function idToPkmnBundleName(index) {
87 | return (
88 | "pm" + (index >= 10 ? index >= 100 ? "0" : "00" : "000") + index
89 | );
90 | }
91 |
92 | /**
93 | * @param {String} str
94 | * @return {String}
95 | */
96 | export function capitalize(str) {
97 | return (
98 | str[0].toUpperCase() + str.slice(1)
99 | );
100 | }
101 |
102 | /**
103 | * @param {String} str
104 | * @return {String}
105 | */
106 | export function deCapitalize(str) {
107 | return (
108 | str[0].toLowerCase() + str.slice(1)
109 | );
110 | }
111 |
112 | let rx_username = /[^a-z\d]/i;
113 | export function validUsername(str) {
114 | return (
115 | !(rx_username.test(str))
116 | );
117 | }
118 |
119 | let rx_email = /\S+@\S+\.\S+/;
120 | export function validEmail(str) {
121 | return (
122 | !!rx_email.test(str)
123 | );
124 | }
125 |
126 | /**
127 | * @param {Request} req
128 | */
129 | export function parseSignature(req) {
130 | let key = pcrypt.decrypt(req.unknown6[0].unknown2.encrypted_signature);
131 | return (
132 | POGOProtos.parseWithUnknown(key, "POGOProtos.Networking.Envelopes.Signature")
133 | );
134 | }
--------------------------------------------------------------------------------
/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 |
4 | [program:sshd]
5 | command=/usr/sbin/sshd -D
6 | autorestart=true
7 |
8 | [program:mysql]
9 | command=/usr/bin/pidproxy /var/run/mysqld/mysqld.pid /usr/sbin/mysqld
10 | autorestart=true
11 |
--------------------------------------------------------------------------------
/updater.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import fse from "fs-extra";
3 | import git from "nodegit";
4 | import dirTree from "directory-tree";
5 |
6 | let tmpDir = "./tmp";
7 | let cloneDir = JSON.parse(fs.readFileSync("./package.json")).repository.url;
8 |
9 | let ignore = [".git", "cfg.js", "updater.js"];
10 |
11 | function skip() {
12 | return new Promise((resolve) => {
13 | fse.removeSync(tmpDir);
14 | console.log(`Skipped update to version ${newVersion}`);
15 | resolve();
16 | });
17 | }
18 |
19 | function updateProject() {
20 | return new Promise((resolve) => {
21 | let newFiles = dirTree("./tmp");
22 | recurse(newFiles.children);
23 | resolve();
24 | });
25 | }
26 |
27 | function recurse(parent) {
28 | for (let key in parent) {
29 | let name = parent[key].name;
30 | if (ignore.includes(name)) continue;
31 | let isDir = parent[key].hasOwnProperty("children");
32 | try {
33 | if (!isDir) {
34 | fse.outputFileSync(parent[key].path.substring(4), fs.readFileSync(parent[key].path));
35 | }
36 | else {
37 | recurse(parent[key].children);
38 | }
39 | } catch (e) { console.log(e); }
40 | };
41 | }
42 |
43 | ((() => new Promise((resolve) => {
44 | console.log("Be aware about the updater is experimental!");
45 | console.log("Preparing to update..");
46 | setTimeout(() => {
47 | fse.removeSync(tmpDir);
48 | let currentVersion = JSON.parse(fs.readFileSync("./package.json")).version;
49 | console.log(`Your current version is ${currentVersion}!`);
50 | setTimeout(() => {
51 | console.log("Fetching latest version..");
52 | git.Clone(cloneDir, tmpDir).then((res, rofl) => {
53 | let newVersion = null;
54 | try {
55 | newVersion = JSON.parse(fs.readFileSync(`${tmpDir}/package.json`)).version;
56 | } catch (e) {
57 | console.log("Version check failed!");
58 | return resolve();
59 | }
60 | if (currentVersion === newVersion) {
61 | console.log(`You are already running the latest version ${currentVersion}!`);
62 | skip().then(resolve);
63 | return void 0;
64 | }
65 | else if (currentVersion > newVersion) {
66 | console.log(`Your version ${currentVersion} is newer than ${newVersion}!`);
67 | skip().then(resolve);
68 | return void 0;
69 | }
70 | console.log(`Latest version is ${newVersion}`);
71 | console.log(`Updating to version ${newVersion}`);
72 | updateProject().then(() => {
73 | fse.removeSync(tmpDir);
74 | console.log("Update successfully completed, please restart!");
75 | resolve();
76 | });
77 | });
78 | }, 2e3);
79 |
80 | }, 1e3);
81 | })))();
--------------------------------------------------------------------------------