├── website
├── js
│ ├── _util.js
│ ├── game.js
│ ├── server.js
│ ├── players.js
│ └── main.js
├── views
│ ├── empty.pug
│ ├── _partials
│ │ ├── clans-online.pug
│ │ ├── similar-games.pug
│ │ └── player-search-results.pug
│ ├── stats.pug
│ ├── profile.pug
│ ├── game.pug
│ ├── error.pug
│ ├── metrics.pug
│ └── server.pug
└── styles
│ └── _nprogress.scss
├── assets
├── images
│ ├── 6.jpg
│ ├── squid.png
│ ├── flags
│ │ ├── AD.png
│ │ ├── AE.png
│ │ ├── AF.png
│ │ ├── AG.png
│ │ ├── AI.png
│ │ ├── AL.png
│ │ ├── AM.png
│ │ ├── AN.png
│ │ ├── AO.png
│ │ ├── AQ.png
│ │ ├── AR.png
│ │ ├── AS.png
│ │ ├── AT.png
│ │ ├── AU.png
│ │ ├── AW.png
│ │ ├── AZ.png
│ │ ├── BA.png
│ │ ├── BB.png
│ │ ├── BD.png
│ │ ├── BE.png
│ │ ├── BF.png
│ │ ├── BG.png
│ │ ├── BH.png
│ │ ├── BI.png
│ │ ├── BJ.png
│ │ ├── BM.png
│ │ ├── BN.png
│ │ ├── BO.png
│ │ ├── BR.png
│ │ ├── BS.png
│ │ ├── BT.png
│ │ ├── BW.png
│ │ ├── BY.png
│ │ ├── BZ.png
│ │ ├── CA.png
│ │ ├── CD.png
│ │ ├── CF.png
│ │ ├── CG.png
│ │ ├── CH.png
│ │ ├── CI.png
│ │ ├── CK.png
│ │ ├── CL.png
│ │ ├── CM.png
│ │ ├── CN.png
│ │ ├── CO.png
│ │ ├── CR.png
│ │ ├── CU.png
│ │ ├── CV.png
│ │ ├── CY.png
│ │ ├── CZ.png
│ │ ├── DE.png
│ │ ├── DJ.png
│ │ ├── DK.png
│ │ ├── DM.png
│ │ ├── DO.png
│ │ ├── DZ.png
│ │ ├── EC.png
│ │ ├── EE.png
│ │ ├── EG.png
│ │ ├── EH.png
│ │ ├── ER.png
│ │ ├── ES.png
│ │ ├── ET.png
│ │ ├── EU.png
│ │ ├── FI.png
│ │ ├── FJ.png
│ │ ├── FM.png
│ │ ├── FO.png
│ │ ├── FR.png
│ │ ├── GA.png
│ │ ├── GB.png
│ │ ├── GD.png
│ │ ├── GE.png
│ │ ├── GG.png
│ │ ├── GH.png
│ │ ├── GI.png
│ │ ├── GL.png
│ │ ├── GM.png
│ │ ├── GN.png
│ │ ├── GP.png
│ │ ├── GQ.png
│ │ ├── GR.png
│ │ ├── GT.png
│ │ ├── GU.png
│ │ ├── GW.png
│ │ ├── GY.png
│ │ ├── HK.png
│ │ ├── HN.png
│ │ ├── HR.png
│ │ ├── HT.png
│ │ ├── HU.png
│ │ ├── ID.png
│ │ ├── IE.png
│ │ ├── IL.png
│ │ ├── IM.png
│ │ ├── IN.png
│ │ ├── IQ.png
│ │ ├── IR.png
│ │ ├── IS.png
│ │ ├── IT.png
│ │ ├── JE.png
│ │ ├── JM.png
│ │ ├── JO.png
│ │ ├── JP.png
│ │ ├── KE.png
│ │ ├── KG.png
│ │ ├── KH.png
│ │ ├── KI.png
│ │ ├── KM.png
│ │ ├── KN.png
│ │ ├── KP.png
│ │ ├── KR.png
│ │ ├── KW.png
│ │ ├── KY.png
│ │ ├── KZ.png
│ │ ├── LA.png
│ │ ├── LB.png
│ │ ├── LC.png
│ │ ├── LI.png
│ │ ├── LK.png
│ │ ├── LR.png
│ │ ├── LS.png
│ │ ├── LT.png
│ │ ├── LU.png
│ │ ├── LV.png
│ │ ├── LY.png
│ │ ├── MA.png
│ │ ├── MC.png
│ │ ├── MD.png
│ │ ├── ME.png
│ │ ├── MG.png
│ │ ├── MH.png
│ │ ├── MK.png
│ │ ├── ML.png
│ │ ├── MM.png
│ │ ├── MN.png
│ │ ├── MO.png
│ │ ├── MQ.png
│ │ ├── MR.png
│ │ ├── MS.png
│ │ ├── MT.png
│ │ ├── MU.png
│ │ ├── MV.png
│ │ ├── MW.png
│ │ ├── MX.png
│ │ ├── MY.png
│ │ ├── MZ.png
│ │ ├── NA.png
│ │ ├── NC.png
│ │ ├── NE.png
│ │ ├── NG.png
│ │ ├── NI.png
│ │ ├── NL.png
│ │ ├── NO.png
│ │ ├── NP.png
│ │ ├── NR.png
│ │ ├── NZ.png
│ │ ├── OM.png
│ │ ├── PA.png
│ │ ├── PE.png
│ │ ├── PF.png
│ │ ├── PG.png
│ │ ├── PH.png
│ │ ├── PK.png
│ │ ├── PL.png
│ │ ├── PR.png
│ │ ├── PS.png
│ │ ├── PT.png
│ │ ├── PW.png
│ │ ├── PY.png
│ │ ├── QA.png
│ │ ├── RE.png
│ │ ├── RO.png
│ │ ├── RS.png
│ │ ├── RU.png
│ │ ├── RW.png
│ │ ├── SA.png
│ │ ├── SB.png
│ │ ├── SC.png
│ │ ├── SD.png
│ │ ├── SE.png
│ │ ├── SG.png
│ │ ├── SI.png
│ │ ├── SK.png
│ │ ├── SL.png
│ │ ├── SM.png
│ │ ├── SN.png
│ │ ├── SO.png
│ │ ├── SR.png
│ │ ├── ST.png
│ │ ├── SV.png
│ │ ├── SY.png
│ │ ├── SZ.png
│ │ ├── TC.png
│ │ ├── TD.png
│ │ ├── TG.png
│ │ ├── TH.png
│ │ ├── TJ.png
│ │ ├── TL.png
│ │ ├── TM.png
│ │ ├── TN.png
│ │ ├── TO.png
│ │ ├── TR.png
│ │ ├── TT.png
│ │ ├── TV.png
│ │ ├── TW.png
│ │ ├── TZ.png
│ │ ├── UA.png
│ │ ├── UG.png
│ │ ├── US.png
│ │ ├── UY.png
│ │ ├── UZ.png
│ │ ├── VA.png
│ │ ├── VC.png
│ │ ├── VE.png
│ │ ├── VG.png
│ │ ├── VI.png
│ │ ├── VN.png
│ │ ├── VU.png
│ │ ├── WS.png
│ │ ├── YE.png
│ │ ├── ZA.png
│ │ ├── ZM.png
│ │ └── ZW.png
│ ├── icons
│ │ ├── mp5.png
│ │ ├── flag.png
│ │ ├── skull.png
│ │ ├── backstab.png
│ │ ├── crosshair.png
│ │ └── LICENSE
│ ├── mapshots
│ │ ├── ot.jpg
│ │ ├── ow.jpg
│ │ ├── ra.jpg
│ │ ├── abbey.jpg
│ │ ├── abyss.jpg
│ │ ├── alloy.jpg
│ │ ├── antel.jpg
│ │ ├── aod.jpg
│ │ ├── bklyn.jpg
│ │ ├── campo.jpg
│ │ ├── casa.jpg
│ │ ├── clash.jpg
│ │ ├── croma.jpg
│ │ ├── depot.jpg
│ │ ├── disc.jpg
│ │ ├── dock.jpg
│ │ ├── donya.jpg
│ │ ├── douze.jpg
│ │ ├── duel5.jpg
│ │ ├── duel7.jpg
│ │ ├── duel8.jpg
│ │ ├── dune.jpg
│ │ ├── duomo.jpg
│ │ ├── dust2.jpg
│ │ ├── dust4.jpg
│ │ ├── dust6.jpg
│ │ ├── elegy.jpg
│ │ ├── eris.jpg
│ │ ├── exist.jpg
│ │ ├── exo.jpg
│ │ ├── fc3.jpg
│ │ ├── fc4.jpg
│ │ ├── fc5.jpg
│ │ ├── fdm6.jpg
│ │ ├── force.jpg
│ │ ├── forge.jpg
│ │ ├── frag2.jpg
│ │ ├── fury.jpg
│ │ ├── gorge.jpg
│ │ ├── gubo.jpg
│ │ ├── hades.jpg
│ │ ├── hallo.jpg
│ │ ├── hashi.jpg
│ │ ├── haste.jpg
│ │ ├── hator.jpg
│ │ ├── haze.jpg
│ │ ├── hdm3.jpg
│ │ ├── hog2.jpg
│ │ ├── horus.jpg
│ │ ├── idris.jpg
│ │ ├── kffa.jpg
│ │ ├── kiryu.jpg
│ │ ├── kmap5.jpg
│ │ ├── ks2.jpg
│ │ ├── l_ctf.jpg
│ │ ├── lost.jpg
│ │ ├── luna.jpg
│ │ ├── mach2.jpg
│ │ ├── maple.jpg
│ │ ├── masdm.jpg
│ │ ├── mbt1.jpg
│ │ ├── mbt10.jpg
│ │ ├── mbt12.jpg
│ │ ├── mbt2.jpg
│ │ ├── mbt4.jpg
│ │ ├── mbt9.jpg
│ │ ├── metl2.jpg
│ │ ├── metl3.jpg
│ │ ├── metl4.jpg
│ │ ├── mill.jpg
│ │ ├── nitro.jpg
│ │ ├── nmp4.jpg
│ │ ├── nmp8.jpg
│ │ ├── nmp9.jpg
│ │ ├── oasis.jpg
│ │ ├── orbe.jpg
│ │ ├── orion.jpg
│ │ ├── park.jpg
│ │ ├── pgdm.jpg
│ │ ├── regal.jpg
│ │ ├── relic.jpg
│ │ ├── risk.jpg
│ │ ├── rkc2.jpg
│ │ ├── rm1.jpg
│ │ ├── rm5.jpg
│ │ ├── ruby.jpg
│ │ ├── ruine.jpg
│ │ ├── rust.jpg
│ │ ├── sdm1.jpg
│ │ ├── shiva.jpg
│ │ ├── siege.jpg
│ │ ├── souls.jpg
│ │ ├── spcr.jpg
│ │ ├── spcr2.jpg
│ │ ├── star.jpg
│ │ ├── surge.jpg
│ │ ├── tejen.jpg
│ │ ├── thor.jpg
│ │ ├── tubes.jpg
│ │ ├── wake5.jpg
│ │ ├── waltz.jpg
│ │ ├── wdcd.jpg
│ │ ├── xenon.jpg
│ │ ├── zdm2.jpg
│ │ ├── ztn.jpg
│ │ ├── DM_BS1.jpg
│ │ ├── aard3c.jpg
│ │ ├── academy.jpg
│ │ ├── access.jpg
│ │ ├── alithia.jpg
│ │ ├── anubis.jpg
│ │ ├── arabic.jpg
│ │ ├── arbana.jpg
│ │ ├── asgard.jpg
│ │ ├── asthma.jpg
│ │ ├── autumn.jpg
│ │ ├── averas.jpg
│ │ ├── awoken.jpg
│ │ ├── bvdm_01.jpg
│ │ ├── c_egypt.jpg
│ │ ├── canyon.jpg
│ │ ├── carbide.jpg
│ │ ├── cartel.jpg
│ │ ├── catch22.jpg
│ │ ├── collide.jpg
│ │ ├── colony.jpg
│ │ ├── complex.jpg
│ │ ├── crnsp1.jpg
│ │ ├── crust1.jpg
│ │ ├── crust2.jpg
│ │ ├── crypta.jpg
│ │ ├── curvedm.jpg
│ │ ├── daemex.jpg
│ │ ├── destiny.jpg
│ │ ├── dispute.jpg
│ │ ├── enigma.jpg
│ │ ├── fallen.jpg
│ │ ├── frozen.jpg
│ │ ├── fubuki.jpg
│ │ ├── fusion.jpg
│ │ ├── garden.jpg
│ │ ├── genesis.jpg
│ │ ├── ghetto.jpg
│ │ ├── hektik.jpg
│ │ ├── hidden.jpg
│ │ ├── idyll3.jpg
│ │ ├── infamy.jpg
│ │ ├── insipid.jpg
│ │ ├── island.jpg
│ │ ├── janela.jpg
│ │ ├── justice.jpg
│ │ ├── k_rpg1.jpg
│ │ ├── kastro.jpg
│ │ ├── ksauer1.jpg
│ │ ├── ladder.jpg
│ │ ├── laucin.jpg
│ │ ├── legacy.jpg
│ │ ├── legazzo.jpg
│ │ ├── level9.jpg
│ │ ├── mc-lab.jpg
│ │ ├── memento.jpg
│ │ ├── memoria.jpg
│ │ ├── mercury.jpg
│ │ ├── mpsp10.jpg
│ │ ├── mpsp6a.jpg
│ │ ├── mpsp6b.jpg
│ │ ├── mpsp6c.jpg
│ │ ├── mpsp9a.jpg
│ │ ├── mpsp9b.jpg
│ │ ├── mpsp9c.jpg
│ │ ├── nessus.jpg
│ │ ├── nevil_c.jpg
│ │ ├── nucleus.jpg
│ │ ├── ognjen.jpg
│ │ ├── osiris.jpg
│ │ ├── outpost.jpg
│ │ ├── pandora.jpg
│ │ ├── pariah.jpg
│ │ ├── pul1ctf.jpg
│ │ ├── refuge.jpg
│ │ ├── reissen.jpg
│ │ ├── river_c.jpg
│ │ ├── rpg_01.jpg
│ │ ├── ruebli.jpg
│ │ ├── saffier.jpg
│ │ ├── shindou.jpg
│ │ ├── siberia.jpg
│ │ ├── skrdm1.jpg
│ │ ├── skrsp1.jpg
│ │ ├── spiralz.jpg
│ │ ├── stadium.jpg
│ │ ├── stemple.jpg
│ │ ├── stolen.jpg
│ │ ├── suburb.jpg
│ │ ├── suisei.jpg
│ │ ├── tartech.jpg
│ │ ├── tempest.jpg
│ │ ├── torment.jpg
│ │ ├── tortuga.jpg
│ │ ├── tumwalk.jpg
│ │ ├── turbine.jpg
│ │ ├── turmoil.jpg
│ │ ├── unworld.jpg
│ │ ├── urban_c.jpg
│ │ ├── venice.jpg
│ │ ├── warlock.jpg
│ │ ├── zavial.jpg
│ │ ├── akaritori.jpg
│ │ ├── akimiski.jpg
│ │ ├── akroseum.jpg
│ │ ├── albatross.jpg
│ │ ├── aqueducts.jpg
│ │ ├── asenatra.jpg
│ │ ├── asteroids.jpg
│ │ ├── authentic.jpg
│ │ ├── bad_moon.jpg
│ │ ├── box_demo.jpg
│ │ ├── breakout.jpg
│ │ ├── bt_falls.jpg
│ │ ├── c_valley.jpg
│ │ ├── caribbean.jpg
│ │ ├── collusion.jpg
│ │ ├── conflict.jpg
│ │ ├── corruption.jpg
│ │ ├── ctf_suite.jpg
│ │ ├── cwcastle.jpg
│ │ ├── damnation.jpg
│ │ ├── darkdeath.jpg
│ │ ├── deathtek.jpg
│ │ ├── dirtndust.jpg
│ │ ├── disruption.jpg
│ │ ├── door_demo.jpg
│ │ ├── dopamine.jpg
│ │ ├── earthsea.jpg
│ │ ├── europium.jpg
│ │ ├── evilness.jpg
│ │ ├── ex_example.jpg
│ │ ├── fb_capture.jpg
│ │ ├── ferguson.jpg
│ │ ├── fire_keep.jpg
│ │ ├── flagstone.jpg
│ │ ├── forgotten.jpg
│ │ ├── fortress.jpg
│ │ ├── frag-lab.jpg
│ │ ├── fragnostic.jpg
│ │ ├── fragplaza.jpg
│ │ ├── frostbyte.jpg
│ │ ├── frozen6a.jpg
│ │ ├── gothic-df.jpg
│ │ ├── guacamole.jpg
│ │ ├── gunmetal.jpg
│ │ ├── headroom.jpg
│ │ ├── helligsted.jpg
│ │ ├── hillfort.jpg
│ │ ├── industry.jpg
│ │ ├── infernal.jpg
│ │ ├── injustice.jpg
│ │ ├── kalking1.jpg
│ │ ├── katrez_d.jpg
│ │ ├── killcore3.jpg
│ │ ├── konkuri-to.jpg
│ │ ├── kopenhagen.jpg
│ │ ├── lost_soul.jpg
│ │ ├── lost_world.jpg
│ │ ├── meltdown2.jpg
│ │ ├── memento2b.jpg
│ │ ├── monastery.jpg
│ │ ├── moonlite.jpg
│ │ ├── neonpanic.jpg
│ │ ├── nevil_c2.jpg
│ │ ├── new_energy.jpg
│ │ ├── oddworld.jpg
│ │ ├── ogrosupply.jpg
│ │ ├── oldschool.jpg
│ │ ├── overdrive.jpg
│ │ ├── paradigm.jpg
│ │ ├── ph-capture.jpg
│ │ ├── phosgene.jpg
│ │ ├── phrantic.jpg
│ │ ├── powerplant.jpg
│ │ ├── purgatory.jpg
│ │ ├── recovery.jpg
│ │ ├── redemption.jpg
│ │ ├── renegade.jpg
│ │ ├── river_keep.jpg
│ │ ├── roughinery.jpg
│ │ ├── sacrifice.jpg
│ │ ├── sandstorm.jpg
│ │ ├── sandstorm2.jpg
│ │ ├── sauerowalk.jpg
│ │ ├── serenity.jpg
│ │ ├── shadowed.jpg
│ │ ├── shinmei1.jpg
│ │ ├── shipwreck.jpg
│ │ ├── simplicity.jpg
│ │ ├── stahlbox.jpg
│ │ ├── stronghold.jpg
│ │ ├── subterra.jpg
│ │ ├── tatooine.jpg
│ │ ├── teahupoo.jpg
│ │ ├── tectonic.jpg
│ │ ├── thetowers.jpg
│ │ ├── toxicity.jpg
│ │ ├── triforts.jpg
│ │ ├── turbulence.jpg
│ │ ├── twinforts.jpg
│ │ ├── unworld2.jpg
│ │ ├── unworld3.jpg
│ │ ├── valhalla.jpg
│ │ ├── ventania.jpg
│ │ ├── berlin_wall.jpg
│ │ ├── castle_trap.jpg
│ │ ├── core_refuge.jpg
│ │ ├── curvy_castle.jpg
│ │ ├── desecration.jpg
│ │ ├── earthstation.jpg
│ │ ├── face-capture.jpg
│ │ ├── firstevermap.jpg
│ │ ├── killfactory.jpg
│ │ ├── lostinspace.jpg
│ │ ├── pitch_black.jpg
│ │ ├── sauerbraten.jpg
│ │ ├── sauerstruck.jpg
│ │ ├── shellshock2.jpg
│ │ ├── capture_night.jpg
│ │ ├── core_transfer.jpg
│ │ ├── eternal_valley.jpg
│ │ ├── fanatic_quake.jpg
│ │ ├── hiddenfortress.jpg
│ │ ├── neondevastation.jpg
│ │ ├── platform_demo.jpg
│ │ ├── secondevermap.jpg
│ │ └── snapper_rocks.jpg
│ ├── 213425634sad.png
│ └── convert.sh
└── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── .babelrc
├── IP2LOCATION-LITE-DB1.BIN
├── .prettierrc
├── docker-entrypoint.sh
├── src
├── api
│ ├── v2
│ │ ├── main.js
│ │ ├── servers.js
│ │ ├── server.js
│ │ ├── clans.js
│ │ └── players.js
│ ├── v1
│ │ ├── main.js
│ │ ├── servers.js
│ │ └── clan.js
│ └── team-balance.js
├── util
│ ├── database.js
│ ├── redis.js
│ ├── country.js
│ ├── config.js
│ ├── protocol.js
│ ├── metrics.js
│ ├── web.js
│ └── cache.js
├── web
│ ├── banners.js
│ ├── metrics.js
│ ├── index.js
│ ├── servers.js
│ ├── clan.js
│ ├── game.js
│ ├── server.js
│ ├── stats.js
│ └── player.js
├── index.js
├── tracker
│ ├── spy.js
│ ├── master.js
│ └── player-manager.js
└── scripts
│ ├── detect-clanwars.js
│ └── recalculate-elo.js
├── .gitignore
├── knexfile.js
├── .dockerignore
├── Dockerfile
├── migrations
├── 20160510143720_remove_server_keep.js
├── 20171007201942_unique_players_name.js
├── 20180215000343_delete_requests_table.js
├── 20180214035158_update_players_schema.js
├── 20171211131355_add_constraints.js
├── 20180214041134_update_players_schema_2.js
└── 20161202143509_update_mode_names.js
├── docker-compose.yml
├── webpack.config.js
└── package.json
/website/js/_util.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/6.jpg
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-node"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/IP2LOCATION-LITE-DB1.BIN:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/IP2LOCATION-LITE-DB1.BIN
--------------------------------------------------------------------------------
/assets/images/squid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/squid.png
--------------------------------------------------------------------------------
/assets/images/flags/AD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AD.png
--------------------------------------------------------------------------------
/assets/images/flags/AE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AE.png
--------------------------------------------------------------------------------
/assets/images/flags/AF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AF.png
--------------------------------------------------------------------------------
/assets/images/flags/AG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AG.png
--------------------------------------------------------------------------------
/assets/images/flags/AI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AI.png
--------------------------------------------------------------------------------
/assets/images/flags/AL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AL.png
--------------------------------------------------------------------------------
/assets/images/flags/AM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AM.png
--------------------------------------------------------------------------------
/assets/images/flags/AN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AN.png
--------------------------------------------------------------------------------
/assets/images/flags/AO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AO.png
--------------------------------------------------------------------------------
/assets/images/flags/AQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AQ.png
--------------------------------------------------------------------------------
/assets/images/flags/AR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AR.png
--------------------------------------------------------------------------------
/assets/images/flags/AS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AS.png
--------------------------------------------------------------------------------
/assets/images/flags/AT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AT.png
--------------------------------------------------------------------------------
/assets/images/flags/AU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AU.png
--------------------------------------------------------------------------------
/assets/images/flags/AW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AW.png
--------------------------------------------------------------------------------
/assets/images/flags/AZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/AZ.png
--------------------------------------------------------------------------------
/assets/images/flags/BA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BA.png
--------------------------------------------------------------------------------
/assets/images/flags/BB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BB.png
--------------------------------------------------------------------------------
/assets/images/flags/BD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BD.png
--------------------------------------------------------------------------------
/assets/images/flags/BE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BE.png
--------------------------------------------------------------------------------
/assets/images/flags/BF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BF.png
--------------------------------------------------------------------------------
/assets/images/flags/BG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BG.png
--------------------------------------------------------------------------------
/assets/images/flags/BH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BH.png
--------------------------------------------------------------------------------
/assets/images/flags/BI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BI.png
--------------------------------------------------------------------------------
/assets/images/flags/BJ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BJ.png
--------------------------------------------------------------------------------
/assets/images/flags/BM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BM.png
--------------------------------------------------------------------------------
/assets/images/flags/BN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BN.png
--------------------------------------------------------------------------------
/assets/images/flags/BO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BO.png
--------------------------------------------------------------------------------
/assets/images/flags/BR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BR.png
--------------------------------------------------------------------------------
/assets/images/flags/BS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BS.png
--------------------------------------------------------------------------------
/assets/images/flags/BT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BT.png
--------------------------------------------------------------------------------
/assets/images/flags/BW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BW.png
--------------------------------------------------------------------------------
/assets/images/flags/BY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BY.png
--------------------------------------------------------------------------------
/assets/images/flags/BZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/BZ.png
--------------------------------------------------------------------------------
/assets/images/flags/CA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CA.png
--------------------------------------------------------------------------------
/assets/images/flags/CD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CD.png
--------------------------------------------------------------------------------
/assets/images/flags/CF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CF.png
--------------------------------------------------------------------------------
/assets/images/flags/CG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CG.png
--------------------------------------------------------------------------------
/assets/images/flags/CH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CH.png
--------------------------------------------------------------------------------
/assets/images/flags/CI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CI.png
--------------------------------------------------------------------------------
/assets/images/flags/CK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CK.png
--------------------------------------------------------------------------------
/assets/images/flags/CL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CL.png
--------------------------------------------------------------------------------
/assets/images/flags/CM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CM.png
--------------------------------------------------------------------------------
/assets/images/flags/CN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CN.png
--------------------------------------------------------------------------------
/assets/images/flags/CO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CO.png
--------------------------------------------------------------------------------
/assets/images/flags/CR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CR.png
--------------------------------------------------------------------------------
/assets/images/flags/CU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CU.png
--------------------------------------------------------------------------------
/assets/images/flags/CV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CV.png
--------------------------------------------------------------------------------
/assets/images/flags/CY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CY.png
--------------------------------------------------------------------------------
/assets/images/flags/CZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/CZ.png
--------------------------------------------------------------------------------
/assets/images/flags/DE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/DE.png
--------------------------------------------------------------------------------
/assets/images/flags/DJ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/DJ.png
--------------------------------------------------------------------------------
/assets/images/flags/DK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/DK.png
--------------------------------------------------------------------------------
/assets/images/flags/DM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/DM.png
--------------------------------------------------------------------------------
/assets/images/flags/DO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/DO.png
--------------------------------------------------------------------------------
/assets/images/flags/DZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/DZ.png
--------------------------------------------------------------------------------
/assets/images/flags/EC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/EC.png
--------------------------------------------------------------------------------
/assets/images/flags/EE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/EE.png
--------------------------------------------------------------------------------
/assets/images/flags/EG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/EG.png
--------------------------------------------------------------------------------
/assets/images/flags/EH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/EH.png
--------------------------------------------------------------------------------
/assets/images/flags/ER.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ER.png
--------------------------------------------------------------------------------
/assets/images/flags/ES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ES.png
--------------------------------------------------------------------------------
/assets/images/flags/ET.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ET.png
--------------------------------------------------------------------------------
/assets/images/flags/EU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/EU.png
--------------------------------------------------------------------------------
/assets/images/flags/FI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/FI.png
--------------------------------------------------------------------------------
/assets/images/flags/FJ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/FJ.png
--------------------------------------------------------------------------------
/assets/images/flags/FM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/FM.png
--------------------------------------------------------------------------------
/assets/images/flags/FO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/FO.png
--------------------------------------------------------------------------------
/assets/images/flags/FR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/FR.png
--------------------------------------------------------------------------------
/assets/images/flags/GA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GA.png
--------------------------------------------------------------------------------
/assets/images/flags/GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GB.png
--------------------------------------------------------------------------------
/assets/images/flags/GD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GD.png
--------------------------------------------------------------------------------
/assets/images/flags/GE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GE.png
--------------------------------------------------------------------------------
/assets/images/flags/GG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GG.png
--------------------------------------------------------------------------------
/assets/images/flags/GH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GH.png
--------------------------------------------------------------------------------
/assets/images/flags/GI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GI.png
--------------------------------------------------------------------------------
/assets/images/flags/GL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GL.png
--------------------------------------------------------------------------------
/assets/images/flags/GM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GM.png
--------------------------------------------------------------------------------
/assets/images/flags/GN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GN.png
--------------------------------------------------------------------------------
/assets/images/flags/GP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GP.png
--------------------------------------------------------------------------------
/assets/images/flags/GQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GQ.png
--------------------------------------------------------------------------------
/assets/images/flags/GR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GR.png
--------------------------------------------------------------------------------
/assets/images/flags/GT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GT.png
--------------------------------------------------------------------------------
/assets/images/flags/GU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GU.png
--------------------------------------------------------------------------------
/assets/images/flags/GW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GW.png
--------------------------------------------------------------------------------
/assets/images/flags/GY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/GY.png
--------------------------------------------------------------------------------
/assets/images/flags/HK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/HK.png
--------------------------------------------------------------------------------
/assets/images/flags/HN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/HN.png
--------------------------------------------------------------------------------
/assets/images/flags/HR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/HR.png
--------------------------------------------------------------------------------
/assets/images/flags/HT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/HT.png
--------------------------------------------------------------------------------
/assets/images/flags/HU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/HU.png
--------------------------------------------------------------------------------
/assets/images/flags/ID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ID.png
--------------------------------------------------------------------------------
/assets/images/flags/IE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IE.png
--------------------------------------------------------------------------------
/assets/images/flags/IL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IL.png
--------------------------------------------------------------------------------
/assets/images/flags/IM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IM.png
--------------------------------------------------------------------------------
/assets/images/flags/IN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IN.png
--------------------------------------------------------------------------------
/assets/images/flags/IQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IQ.png
--------------------------------------------------------------------------------
/assets/images/flags/IR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IR.png
--------------------------------------------------------------------------------
/assets/images/flags/IS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IS.png
--------------------------------------------------------------------------------
/assets/images/flags/IT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/IT.png
--------------------------------------------------------------------------------
/assets/images/flags/JE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/JE.png
--------------------------------------------------------------------------------
/assets/images/flags/JM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/JM.png
--------------------------------------------------------------------------------
/assets/images/flags/JO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/JO.png
--------------------------------------------------------------------------------
/assets/images/flags/JP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/JP.png
--------------------------------------------------------------------------------
/assets/images/flags/KE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KE.png
--------------------------------------------------------------------------------
/assets/images/flags/KG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KG.png
--------------------------------------------------------------------------------
/assets/images/flags/KH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KH.png
--------------------------------------------------------------------------------
/assets/images/flags/KI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KI.png
--------------------------------------------------------------------------------
/assets/images/flags/KM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KM.png
--------------------------------------------------------------------------------
/assets/images/flags/KN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KN.png
--------------------------------------------------------------------------------
/assets/images/flags/KP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KP.png
--------------------------------------------------------------------------------
/assets/images/flags/KR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KR.png
--------------------------------------------------------------------------------
/assets/images/flags/KW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KW.png
--------------------------------------------------------------------------------
/assets/images/flags/KY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KY.png
--------------------------------------------------------------------------------
/assets/images/flags/KZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/KZ.png
--------------------------------------------------------------------------------
/assets/images/flags/LA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LA.png
--------------------------------------------------------------------------------
/assets/images/flags/LB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LB.png
--------------------------------------------------------------------------------
/assets/images/flags/LC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LC.png
--------------------------------------------------------------------------------
/assets/images/flags/LI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LI.png
--------------------------------------------------------------------------------
/assets/images/flags/LK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LK.png
--------------------------------------------------------------------------------
/assets/images/flags/LR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LR.png
--------------------------------------------------------------------------------
/assets/images/flags/LS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LS.png
--------------------------------------------------------------------------------
/assets/images/flags/LT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LT.png
--------------------------------------------------------------------------------
/assets/images/flags/LU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LU.png
--------------------------------------------------------------------------------
/assets/images/flags/LV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LV.png
--------------------------------------------------------------------------------
/assets/images/flags/LY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/LY.png
--------------------------------------------------------------------------------
/assets/images/flags/MA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MA.png
--------------------------------------------------------------------------------
/assets/images/flags/MC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MC.png
--------------------------------------------------------------------------------
/assets/images/flags/MD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MD.png
--------------------------------------------------------------------------------
/assets/images/flags/ME.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ME.png
--------------------------------------------------------------------------------
/assets/images/flags/MG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MG.png
--------------------------------------------------------------------------------
/assets/images/flags/MH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MH.png
--------------------------------------------------------------------------------
/assets/images/flags/MK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MK.png
--------------------------------------------------------------------------------
/assets/images/flags/ML.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ML.png
--------------------------------------------------------------------------------
/assets/images/flags/MM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MM.png
--------------------------------------------------------------------------------
/assets/images/flags/MN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MN.png
--------------------------------------------------------------------------------
/assets/images/flags/MO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MO.png
--------------------------------------------------------------------------------
/assets/images/flags/MQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MQ.png
--------------------------------------------------------------------------------
/assets/images/flags/MR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MR.png
--------------------------------------------------------------------------------
/assets/images/flags/MS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MS.png
--------------------------------------------------------------------------------
/assets/images/flags/MT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MT.png
--------------------------------------------------------------------------------
/assets/images/flags/MU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MU.png
--------------------------------------------------------------------------------
/assets/images/flags/MV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MV.png
--------------------------------------------------------------------------------
/assets/images/flags/MW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MW.png
--------------------------------------------------------------------------------
/assets/images/flags/MX.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MX.png
--------------------------------------------------------------------------------
/assets/images/flags/MY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MY.png
--------------------------------------------------------------------------------
/assets/images/flags/MZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/MZ.png
--------------------------------------------------------------------------------
/assets/images/flags/NA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NA.png
--------------------------------------------------------------------------------
/assets/images/flags/NC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NC.png
--------------------------------------------------------------------------------
/assets/images/flags/NE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NE.png
--------------------------------------------------------------------------------
/assets/images/flags/NG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NG.png
--------------------------------------------------------------------------------
/assets/images/flags/NI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NI.png
--------------------------------------------------------------------------------
/assets/images/flags/NL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NL.png
--------------------------------------------------------------------------------
/assets/images/flags/NO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NO.png
--------------------------------------------------------------------------------
/assets/images/flags/NP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NP.png
--------------------------------------------------------------------------------
/assets/images/flags/NR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NR.png
--------------------------------------------------------------------------------
/assets/images/flags/NZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/NZ.png
--------------------------------------------------------------------------------
/assets/images/flags/OM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/OM.png
--------------------------------------------------------------------------------
/assets/images/flags/PA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PA.png
--------------------------------------------------------------------------------
/assets/images/flags/PE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PE.png
--------------------------------------------------------------------------------
/assets/images/flags/PF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PF.png
--------------------------------------------------------------------------------
/assets/images/flags/PG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PG.png
--------------------------------------------------------------------------------
/assets/images/flags/PH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PH.png
--------------------------------------------------------------------------------
/assets/images/flags/PK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PK.png
--------------------------------------------------------------------------------
/assets/images/flags/PL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PL.png
--------------------------------------------------------------------------------
/assets/images/flags/PR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PR.png
--------------------------------------------------------------------------------
/assets/images/flags/PS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PS.png
--------------------------------------------------------------------------------
/assets/images/flags/PT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PT.png
--------------------------------------------------------------------------------
/assets/images/flags/PW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PW.png
--------------------------------------------------------------------------------
/assets/images/flags/PY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/PY.png
--------------------------------------------------------------------------------
/assets/images/flags/QA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/QA.png
--------------------------------------------------------------------------------
/assets/images/flags/RE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/RE.png
--------------------------------------------------------------------------------
/assets/images/flags/RO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/RO.png
--------------------------------------------------------------------------------
/assets/images/flags/RS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/RS.png
--------------------------------------------------------------------------------
/assets/images/flags/RU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/RU.png
--------------------------------------------------------------------------------
/assets/images/flags/RW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/RW.png
--------------------------------------------------------------------------------
/assets/images/flags/SA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SA.png
--------------------------------------------------------------------------------
/assets/images/flags/SB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SB.png
--------------------------------------------------------------------------------
/assets/images/flags/SC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SC.png
--------------------------------------------------------------------------------
/assets/images/flags/SD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SD.png
--------------------------------------------------------------------------------
/assets/images/flags/SE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SE.png
--------------------------------------------------------------------------------
/assets/images/flags/SG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SG.png
--------------------------------------------------------------------------------
/assets/images/flags/SI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SI.png
--------------------------------------------------------------------------------
/assets/images/flags/SK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SK.png
--------------------------------------------------------------------------------
/assets/images/flags/SL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SL.png
--------------------------------------------------------------------------------
/assets/images/flags/SM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SM.png
--------------------------------------------------------------------------------
/assets/images/flags/SN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SN.png
--------------------------------------------------------------------------------
/assets/images/flags/SO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SO.png
--------------------------------------------------------------------------------
/assets/images/flags/SR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SR.png
--------------------------------------------------------------------------------
/assets/images/flags/ST.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ST.png
--------------------------------------------------------------------------------
/assets/images/flags/SV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SV.png
--------------------------------------------------------------------------------
/assets/images/flags/SY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SY.png
--------------------------------------------------------------------------------
/assets/images/flags/SZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/SZ.png
--------------------------------------------------------------------------------
/assets/images/flags/TC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TC.png
--------------------------------------------------------------------------------
/assets/images/flags/TD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TD.png
--------------------------------------------------------------------------------
/assets/images/flags/TG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TG.png
--------------------------------------------------------------------------------
/assets/images/flags/TH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TH.png
--------------------------------------------------------------------------------
/assets/images/flags/TJ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TJ.png
--------------------------------------------------------------------------------
/assets/images/flags/TL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TL.png
--------------------------------------------------------------------------------
/assets/images/flags/TM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TM.png
--------------------------------------------------------------------------------
/assets/images/flags/TN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TN.png
--------------------------------------------------------------------------------
/assets/images/flags/TO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TO.png
--------------------------------------------------------------------------------
/assets/images/flags/TR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TR.png
--------------------------------------------------------------------------------
/assets/images/flags/TT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TT.png
--------------------------------------------------------------------------------
/assets/images/flags/TV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TV.png
--------------------------------------------------------------------------------
/assets/images/flags/TW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TW.png
--------------------------------------------------------------------------------
/assets/images/flags/TZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/TZ.png
--------------------------------------------------------------------------------
/assets/images/flags/UA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/UA.png
--------------------------------------------------------------------------------
/assets/images/flags/UG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/UG.png
--------------------------------------------------------------------------------
/assets/images/flags/US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/US.png
--------------------------------------------------------------------------------
/assets/images/flags/UY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/UY.png
--------------------------------------------------------------------------------
/assets/images/flags/UZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/UZ.png
--------------------------------------------------------------------------------
/assets/images/flags/VA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/VA.png
--------------------------------------------------------------------------------
/assets/images/flags/VC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/VC.png
--------------------------------------------------------------------------------
/assets/images/flags/VE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/VE.png
--------------------------------------------------------------------------------
/assets/images/flags/VG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/VG.png
--------------------------------------------------------------------------------
/assets/images/flags/VI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/VI.png
--------------------------------------------------------------------------------
/assets/images/flags/VN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/VN.png
--------------------------------------------------------------------------------
/assets/images/flags/VU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/VU.png
--------------------------------------------------------------------------------
/assets/images/flags/WS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/WS.png
--------------------------------------------------------------------------------
/assets/images/flags/YE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/YE.png
--------------------------------------------------------------------------------
/assets/images/flags/ZA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ZA.png
--------------------------------------------------------------------------------
/assets/images/flags/ZM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ZM.png
--------------------------------------------------------------------------------
/assets/images/flags/ZW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/flags/ZW.png
--------------------------------------------------------------------------------
/assets/images/icons/mp5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/icons/mp5.png
--------------------------------------------------------------------------------
/assets/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/assets/images/icons/flag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/icons/flag.png
--------------------------------------------------------------------------------
/assets/images/icons/skull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/icons/skull.png
--------------------------------------------------------------------------------
/assets/images/mapshots/ot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ot.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ow.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ra.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ra.jpg
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/assets/images/213425634sad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/213425634sad.png
--------------------------------------------------------------------------------
/assets/images/icons/backstab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/icons/backstab.png
--------------------------------------------------------------------------------
/assets/images/mapshots/abbey.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/abbey.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/abyss.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/abyss.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/alloy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/alloy.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/antel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/antel.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/aod.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/aod.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/bklyn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/bklyn.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/campo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/campo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/casa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/casa.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/clash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/clash.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/croma.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/croma.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/depot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/depot.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/disc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/disc.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dock.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dock.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/donya.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/donya.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/douze.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/douze.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/duel5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/duel5.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/duel7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/duel7.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/duel8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/duel8.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dune.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dune.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/duomo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/duomo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dust2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dust2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dust4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dust4.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dust6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dust6.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/elegy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/elegy.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/eris.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/eris.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/exist.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/exist.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/exo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/exo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fc3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fc3.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fc4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fc4.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fc5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fc5.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fdm6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fdm6.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/force.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/force.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/forge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/forge.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/frag2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/frag2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fury.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fury.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/gorge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/gorge.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/gubo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/gubo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hades.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hades.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hallo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hallo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hashi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hashi.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/haste.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/haste.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hator.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hator.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/haze.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/haze.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hdm3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hdm3.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hog2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hog2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/horus.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/horus.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/idris.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/idris.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/kffa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/kffa.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/kiryu.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/kiryu.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/kmap5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/kmap5.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ks2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ks2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/l_ctf.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/l_ctf.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/lost.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/lost.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/luna.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/luna.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mach2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mach2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/maple.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/maple.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/masdm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/masdm.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mbt1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mbt1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mbt10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mbt10.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mbt12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mbt12.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mbt2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mbt2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mbt4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mbt4.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mbt9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mbt9.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/metl2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/metl2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/metl3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/metl3.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/metl4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/metl4.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mill.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mill.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nitro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nitro.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nmp4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nmp4.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nmp8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nmp8.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nmp9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nmp9.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/oasis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/oasis.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/orbe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/orbe.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/orion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/orion.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/park.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/park.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/pgdm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/pgdm.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/regal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/regal.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/relic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/relic.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/risk.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/risk.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/rkc2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/rkc2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/rm1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/rm1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/rm5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/rm5.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ruby.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ruby.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ruine.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ruine.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/rust.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/rust.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/sdm1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/sdm1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/shiva.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/shiva.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/siege.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/siege.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/souls.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/souls.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/spcr.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/spcr.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/spcr2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/spcr2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/star.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/star.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/surge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/surge.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tejen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tejen.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/thor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/thor.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tubes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tubes.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/wake5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/wake5.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/waltz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/waltz.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/wdcd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/wdcd.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/xenon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/xenon.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/zdm2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/zdm2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ztn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ztn.jpg
--------------------------------------------------------------------------------
/assets/images/icons/crosshair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/icons/crosshair.png
--------------------------------------------------------------------------------
/assets/images/mapshots/DM_BS1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/DM_BS1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/aard3c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/aard3c.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/academy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/academy.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/access.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/access.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/alithia.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/alithia.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/anubis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/anubis.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/arabic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/arabic.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/arbana.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/arbana.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/asgard.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/asgard.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/asthma.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/asthma.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/autumn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/autumn.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/averas.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/averas.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/awoken.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/awoken.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/bvdm_01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/bvdm_01.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/c_egypt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/c_egypt.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/canyon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/canyon.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/carbide.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/carbide.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/cartel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/cartel.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/catch22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/catch22.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/collide.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/collide.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/colony.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/colony.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/complex.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/complex.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/crnsp1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/crnsp1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/crust1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/crust1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/crust2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/crust2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/crypta.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/crypta.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/curvedm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/curvedm.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/daemex.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/daemex.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/destiny.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/destiny.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dispute.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dispute.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/enigma.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/enigma.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fallen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fallen.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/frozen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/frozen.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fubuki.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fubuki.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fusion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fusion.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/garden.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/garden.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/genesis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/genesis.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ghetto.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ghetto.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hektik.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hektik.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hidden.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hidden.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/idyll3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/idyll3.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/infamy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/infamy.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/insipid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/insipid.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/island.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/island.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/janela.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/janela.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/justice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/justice.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/k_rpg1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/k_rpg1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/kastro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/kastro.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ksauer1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ksauer1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ladder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ladder.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/laucin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/laucin.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/legacy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/legacy.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/legazzo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/legazzo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/level9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/level9.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mc-lab.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mc-lab.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/memento.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/memento.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/memoria.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/memoria.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mercury.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mercury.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mpsp10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mpsp10.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mpsp6a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mpsp6a.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mpsp6b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mpsp6b.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mpsp6c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mpsp6c.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mpsp9a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mpsp9a.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mpsp9b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mpsp9b.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/mpsp9c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/mpsp9c.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nessus.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nessus.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nevil_c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nevil_c.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nucleus.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nucleus.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ognjen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ognjen.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/osiris.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/osiris.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/outpost.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/outpost.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/pandora.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/pandora.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/pariah.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/pariah.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/pul1ctf.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/pul1ctf.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/refuge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/refuge.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/reissen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/reissen.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/river_c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/river_c.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/rpg_01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/rpg_01.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ruebli.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ruebli.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/saffier.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/saffier.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/shindou.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/shindou.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/siberia.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/siberia.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/skrdm1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/skrdm1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/skrsp1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/skrsp1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/spiralz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/spiralz.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/stadium.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/stadium.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/stemple.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/stemple.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/stolen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/stolen.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/suburb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/suburb.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/suisei.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/suisei.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tartech.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tartech.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tempest.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tempest.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/torment.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/torment.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tortuga.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tortuga.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tumwalk.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tumwalk.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/turbine.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/turbine.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/turmoil.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/turmoil.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/unworld.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/unworld.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/urban_c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/urban_c.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/venice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/venice.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/warlock.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/warlock.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/zavial.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/zavial.jpg
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/assets/images/mapshots/akaritori.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/akaritori.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/akimiski.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/akimiski.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/akroseum.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/akroseum.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/albatross.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/albatross.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/aqueducts.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/aqueducts.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/asenatra.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/asenatra.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/asteroids.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/asteroids.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/authentic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/authentic.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/bad_moon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/bad_moon.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/box_demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/box_demo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/breakout.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/breakout.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/bt_falls.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/bt_falls.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/c_valley.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/c_valley.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/caribbean.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/caribbean.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/collusion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/collusion.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/conflict.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/conflict.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/corruption.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/corruption.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ctf_suite.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ctf_suite.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/cwcastle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/cwcastle.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/damnation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/damnation.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/darkdeath.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/darkdeath.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/deathtek.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/deathtek.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dirtndust.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dirtndust.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/disruption.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/disruption.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/door_demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/door_demo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/dopamine.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/dopamine.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/earthsea.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/earthsea.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/europium.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/europium.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/evilness.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/evilness.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ex_example.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ex_example.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fb_capture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fb_capture.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ferguson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ferguson.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fire_keep.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fire_keep.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/flagstone.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/flagstone.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/forgotten.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/forgotten.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fortress.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fortress.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/frag-lab.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/frag-lab.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fragnostic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fragnostic.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fragplaza.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fragplaza.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/frostbyte.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/frostbyte.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/frozen6a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/frozen6a.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/gothic-df.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/gothic-df.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/guacamole.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/guacamole.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/gunmetal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/gunmetal.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/headroom.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/headroom.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/helligsted.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/helligsted.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hillfort.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hillfort.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/industry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/industry.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/infernal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/infernal.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/injustice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/injustice.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/kalking1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/kalking1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/katrez_d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/katrez_d.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/killcore3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/killcore3.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/konkuri-to.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/konkuri-to.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/kopenhagen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/kopenhagen.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/lost_soul.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/lost_soul.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/lost_world.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/lost_world.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/meltdown2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/meltdown2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/memento2b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/memento2b.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/monastery.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/monastery.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/moonlite.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/moonlite.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/neonpanic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/neonpanic.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/nevil_c2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/nevil_c2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/new_energy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/new_energy.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/oddworld.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/oddworld.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ogrosupply.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ogrosupply.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/oldschool.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/oldschool.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/overdrive.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/overdrive.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/paradigm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/paradigm.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ph-capture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ph-capture.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/phosgene.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/phosgene.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/phrantic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/phrantic.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/powerplant.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/powerplant.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/purgatory.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/purgatory.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/recovery.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/recovery.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/redemption.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/redemption.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/renegade.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/renegade.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/river_keep.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/river_keep.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/roughinery.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/roughinery.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/sacrifice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/sacrifice.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/sandstorm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/sandstorm.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/sandstorm2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/sandstorm2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/sauerowalk.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/sauerowalk.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/serenity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/serenity.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/shadowed.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/shadowed.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/shinmei1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/shinmei1.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/shipwreck.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/shipwreck.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/simplicity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/simplicity.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/stahlbox.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/stahlbox.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/stronghold.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/stronghold.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/subterra.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/subterra.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tatooine.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tatooine.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/teahupoo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/teahupoo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/tectonic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/tectonic.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/thetowers.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/thetowers.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/toxicity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/toxicity.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/triforts.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/triforts.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/turbulence.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/turbulence.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/twinforts.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/twinforts.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/unworld2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/unworld2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/unworld3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/unworld3.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/valhalla.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/valhalla.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/ventania.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/ventania.jpg
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/assets/images/mapshots/berlin_wall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/berlin_wall.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/castle_trap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/castle_trap.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/core_refuge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/core_refuge.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/curvy_castle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/curvy_castle.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/desecration.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/desecration.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/earthstation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/earthstation.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/face-capture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/face-capture.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/firstevermap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/firstevermap.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/killfactory.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/killfactory.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/lostinspace.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/lostinspace.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/pitch_black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/pitch_black.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/sauerbraten.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/sauerbraten.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/sauerstruck.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/sauerstruck.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/shellshock2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/shellshock2.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/capture_night.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/capture_night.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/core_transfer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/core_transfer.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/eternal_valley.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/eternal_valley.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/fanatic_quake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/fanatic_quake.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/hiddenfortress.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/hiddenfortress.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/neondevastation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/neondevastation.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/platform_demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/platform_demo.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/secondevermap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/secondevermap.jpg
--------------------------------------------------------------------------------
/assets/images/mapshots/snapper_rocks.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AngrySnout/SauerTracker/HEAD/assets/images/mapshots/snapper_rocks.jpg
--------------------------------------------------------------------------------
/assets/images/convert.sh:
--------------------------------------------------------------------------------
1 | for file in mapshots_tmp/*.jpg
2 | do
3 | gm convert -resize 1024x1024! -blur 50x4 "$file" "${file/mapshots_tmp/mapshots_out}"
4 | done
5 |
6 |
--------------------------------------------------------------------------------
/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ./wait-for-it.sh postgres:5432
4 |
5 | if [ "$1" = 'start' ]; then
6 | yarn migrate
7 | yarn start
8 | fi
9 |
10 | exec yarn $@
11 |
--------------------------------------------------------------------------------
/src/api/v2/main.js:
--------------------------------------------------------------------------------
1 | require('./servers');
2 | require('./server');
3 | require('./games');
4 | require('./game');
5 | require('./players');
6 | require('./player');
7 | require('./clans');
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 | build/
4 | assets/js/
5 | tmp/
6 | coverage/
7 | esdoc/
8 | config.json
9 | src/web/compat.js
10 | npm-debug.log
11 | logs/
12 | dist/
13 |
--------------------------------------------------------------------------------
/src/api/v1/main.js:
--------------------------------------------------------------------------------
1 | require('./servers');
2 | require('./server');
3 | require('./games');
4 | require('./game');
5 | require('./players');
6 | require('./player');
7 | require('./clans');
8 | require('./clan');
9 |
--------------------------------------------------------------------------------
/website/views/empty.pug:
--------------------------------------------------------------------------------
1 | extends ./_layout.pug
2 |
3 | block content
4 | .row.main-container
5 | ul.breadcrumbs
6 | li.current= title
7 | .medium-12.columns.end
8 | != content
9 |
--------------------------------------------------------------------------------
/assets/images/icons/LICENSE:
--------------------------------------------------------------------------------
1 | All icons in this directory are licensed under CC BY 3.0 and available on http://game-icons.net.
2 |
3 | Credits:
4 | backstab.png, flag.png, skull.png: Lorc
5 | crosshair.ong, mp5.png: Delapouite
6 |
7 |
--------------------------------------------------------------------------------
/knexfile.js:
--------------------------------------------------------------------------------
1 | const database = {
2 | client: 'pg',
3 | connection: process.env.DATABASE_URL,
4 | searchPath: ['knex', 'public'],
5 | };
6 |
7 | module.exports = {
8 | development: database,
9 | production: database,
10 | };
11 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 | build/
4 | assets/js/
5 | tmp/
6 | coverage/
7 | esdoc/
8 | config.json
9 | src/web/compat.js
10 | npm-debug.log
11 | logs/
12 | dist/
13 | docker-compose.yml
14 | Dockerfile
15 | pgdata/
16 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:13
2 |
3 | WORKDIR /usr/src/app
4 |
5 | COPY package*.json ./
6 |
7 | RUN yarn install
8 |
9 | COPY . .
10 |
11 | RUN yarn build
12 |
13 | EXPOSE 8080
14 |
15 | ENTRYPOINT [ "docker-entrypoint.sh" ]
16 |
17 | CMD [ "start" ]
18 |
--------------------------------------------------------------------------------
/src/util/database.js:
--------------------------------------------------------------------------------
1 | import knex from 'knex';
2 |
3 | import { getDatabaseURL } from '../util/config';
4 |
5 | const database = knex({
6 | client: 'pg',
7 | connection: getDatabaseURL(),
8 | searchPath: ['knex', 'public'],
9 | });
10 | export default database;
11 |
--------------------------------------------------------------------------------
/src/web/banners.js:
--------------------------------------------------------------------------------
1 | import app from '../util/web';
2 | import vars from '../../vars.json';
3 | import bannersTemplate from '../../website/views/banners.pug';
4 |
5 | app.get('/banners', (req, res) => {
6 | res.send(bannersTemplate({ query: req.query, bannerURL: vars.bannerURL }));
7 | });
8 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import startTeamBalanceServer from './api/team-balance';
2 |
3 | require('./tracker/server-manager').default.start();
4 | require('./tracker/player-manager').default.start();
5 |
6 | require('./tracker/master').start();
7 |
8 | require('./web');
9 | startTeamBalanceServer();
10 |
--------------------------------------------------------------------------------
/src/web/metrics.js:
--------------------------------------------------------------------------------
1 | import app from '../util/web';
2 | import { getAllMetrics } from '../util/metrics';
3 | import metricsTemplate from '../../website/views/metrics.pug';
4 |
5 | app.get('/metrics', (req, res) => {
6 | getAllMetrics().then(metrics => {
7 | res.send(metricsTemplate({ metrics }));
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/migrations/20160510143720_remove_server_keep.js:
--------------------------------------------------------------------------------
1 |
2 | exports.up = function(knex, Promise) {
3 | return knex.schema.table('servers', function (table) {
4 | table.dropColumn('keep');
5 | });
6 | };
7 |
8 | exports.down = function(knex, Promise) {
9 | return knex.schema.table('servers', function (table) {
10 | table.boolean('keep');
11 | });
12 | };
13 |
--------------------------------------------------------------------------------
/migrations/20171007201942_unique_players_name.js:
--------------------------------------------------------------------------------
1 |
2 | exports.up = function(knex, Promise) {
3 | return knex.schema.table('players', function (table) {
4 | table.unique('name');
5 | });
6 | };
7 |
8 | exports.down = function(knex, Promise) {
9 | return knex.schema.table('players', function (table) {
10 | table.dropUnique('name');
11 | });
12 | };
13 |
--------------------------------------------------------------------------------
/src/web/index.js:
--------------------------------------------------------------------------------
1 | require('../api/v1/main');
2 | require('../api/v2/main');
3 |
4 | require('./about');
5 | require('./servers');
6 | require('./server');
7 | require('./metrics');
8 | require('./games');
9 | require('./game');
10 | require('./players');
11 | require('./player');
12 | require('./clans');
13 | require('./clan');
14 | require('./stats');
15 | require('./banners');
16 |
--------------------------------------------------------------------------------
/website/views/_partials/clans-online.pug:
--------------------------------------------------------------------------------
1 | if clansOnline && clansOnline.length
2 | label.big Clans online:
3 | for clan in clansOnline
4 | a(title=(clan.players.join(" \n")), onclick=("findPlayer('"+clan.name.replace(/\'/g, "\\'")+"')")) #{clan.name}
5 | |
6 | span.label.secondary(style="margin-top: -5px;") #{clan.count}
7 | |
8 |
--------------------------------------------------------------------------------
/src/util/redis.js:
--------------------------------------------------------------------------------
1 | import redis from 'redis';
2 | import Promise from 'bluebird';
3 |
4 | import { logError } from './util';
5 | import { getRedisURL } from './config';
6 |
7 | Promise.promisifyAll(redis.RedisClient.prototype);
8 | Promise.promisifyAll(redis.Multi.prototype);
9 |
10 |
11 | const client = redis.createClient({
12 | url: getRedisURL()
13 | });
14 | export default client;
15 |
16 | client.on('error', (err) => {
17 | logError(err);
18 | });
19 |
--------------------------------------------------------------------------------
/migrations/20180215000343_delete_requests_table.js:
--------------------------------------------------------------------------------
1 |
2 | exports.up = function(knex, Promise) {
3 | return knex.schema.dropTable('requests');
4 | };
5 |
6 | exports.down = function(knex, Promise) {
7 | return knex.schema.createTableIfNotExists('requests', function (table) {
8 | table.increments();
9 | table.string('method');
10 | table.string('ip');
11 | table.text('url');
12 | table.float('time');
13 | table.dateTime('timestamp').defaultTo(knex.fn.now());
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/src/api/v2/servers.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import Promise from 'bluebird';
3 |
4 | import app from '../../util/web';
5 | import cache from '../../util/cache';
6 | import serverManager from '../../tracker/server-manager';
7 | import redis from '../../util/redis';
8 |
9 | app.get('/api/v2/servers', (req, res) => {
10 | redis.getAsync('server-list')
11 | .then((list) => {
12 | const servers = JSON.parse(list);
13 | res.send(servers);
14 | })
15 | .catch((err) => { res.status(500).send({ error: err }); });
16 | });
17 |
--------------------------------------------------------------------------------
/migrations/20180214035158_update_players_schema.js:
--------------------------------------------------------------------------------
1 |
2 | exports.up = function(knex, Promise) {
3 | return knex.raw('ALTER TABLE players ADD COLUMN "accFrags" double precision; UPDATE players SET "accFrags" = players.acc * players.frags')
4 | .then(() => knex.schema.table('players', function (table) {
5 | table.dropColumn('countryName');
6 | table.dropColumn('acc');
7 | table.dropColumn('kpd');
8 | }));
9 | };
10 |
11 | exports.down = function(knex, Promise) {
12 | return knex.schema.table('players', function (table) {
13 | table.dropColumn('accFrags');
14 | table.text('countryName');
15 | table.float('acc');
16 | table.float('kpd');
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/website/views/stats.pug:
--------------------------------------------------------------------------------
1 | extends ./_layout.pug
2 |
3 | block vars
4 | - section = "home";
5 | - title = "Stats";
6 |
7 | block content
8 | .row.main-container
9 | ul.breadcrumbs
10 | li.current= title
11 | .medium-12.columns.end
12 | label.big Last 7 days' statistics:
13 | table
14 | thead
15 | tr
16 | td(width="25%") Day
17 | td(width="25%", style="text-align: right;") Games
18 | tbody
19 | for day in stats
20 | tr
21 | td= (day.date==today)? "Today": ((day.date==yesterday)? "Yesterday": day.date)
22 | td(style="text-align: right;")= day.games||0
23 |
--------------------------------------------------------------------------------
/src/web/servers.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import vars from '../../vars.json';
4 |
5 | import app from '../util/web';
6 | import redis from '../util/redis';
7 | import serversTemplate from '../../website/views/servers.pug';
8 |
9 | app.get('/', (req, res) => {
10 | redis
11 | .getAsync('server-list')
12 | .then(list => {
13 | res.send(
14 | serversTemplate({
15 | servers: _.orderBy(list ? JSON.parse(list) : [], 'clients', 'desc'),
16 | sortedBy: 'clients',
17 | sortOrder: 'desc',
18 | vars,
19 | noBanner: req.query.banner === 'no',
20 | })
21 | );
22 | })
23 | .catch(err => {
24 | res.status(500).send({ error: err.message });
25 | });
26 | });
27 |
28 | app.get('/servers', (req, res) => {
29 | res.redirect('/');
30 | });
31 |
--------------------------------------------------------------------------------
/src/web/clan.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import vars from '../../vars.json';
4 |
5 | import app from '../util/web';
6 | import { logError, ObjectNotFoundError } from '../util/util';
7 | import { getClan } from '../api/v1/clan';
8 | import clanTemplate from '../../website/views/clan.pug';
9 |
10 | app.get('/clan/:name', (req, res) => {
11 | getClan(req.params.name)
12 | .then(result => {
13 | res.send(
14 | clanTemplate(_.assign(result, { bannerURL: vars.bannerURL, _ }))
15 | );
16 | })
17 | .catch(ObjectNotFoundError, () => {
18 | res
19 | .status(404)
20 | .render('error', { status: 404, error: 'Clan not found.' });
21 | })
22 | .catch(err => {
23 | logError(err);
24 | res.status(500).render('error', { status: 500, error: err.message });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/src/web/game.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import vars from '../../vars.json';
4 |
5 | import app from '../util/web';
6 | import { ObjectNotFoundError } from '../util/util';
7 | import { getGame } from '../api/v1/game';
8 | import gameTemplate from '../../website/views/game.pug';
9 |
10 | app.get('/game/:id', (req, res) => {
11 | getGame(req.params.id)
12 | .then(result => {
13 | res.send(
14 | gameTemplate({
15 | id: req.params.id,
16 | server: result,
17 | _,
18 | vars,
19 | })
20 | );
21 | })
22 | .catch(ObjectNotFoundError, () => {
23 | res
24 | .status(404)
25 | .render('error', { status: 404, error: 'Game not found.' });
26 | })
27 | .catch(err => {
28 | res.status(500).render('error', { status: 500, error: err.message });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/website/views/profile.pug:
--------------------------------------------------------------------------------
1 | extends ./_layout.pug
2 |
3 | block vars
4 | - section = "players";
5 | - title = "Profile";
6 |
7 | block content
8 | .row.main-container
9 | ul.breadcrumbs
10 | li.current Profile
11 | .medium-12.columns
12 | if names && names.length
13 | | Possible names for your IP address (#{ip}) are:
14 | div(style="margin-left: 3em")
15 | for name in names
16 | a(href=("/player/"+encodeURIComponent(name)))= name
17 | br
18 | br
19 | | None of these are you? Try searching for your name on the players page.
20 | else
21 | | No names found for your IP address. You can search for your name on the players page.
22 |
--------------------------------------------------------------------------------
/src/util/country.js:
--------------------------------------------------------------------------------
1 | import ip2loc from 'ip2location-nodejs';
2 | import countries from 'i18n-iso-countries';
3 |
4 | ip2loc.IP2Location_init('./IP2LOCATION-LITE-DB1.BIN');
5 |
6 | export function formatIP(ipInt) {
7 | return (
8 | (ipInt >>> 24) +
9 | '.' +
10 | ((ipInt >> 16) & 255) +
11 | '.' +
12 | ((ipInt >> 8) & 255) +
13 | '.' +
14 | (ipInt & 255)
15 | );
16 | }
17 |
18 | export default function getCountry(ip) {
19 | if (ip === '2.16.6.0') return 'DE';
20 |
21 | const country = ip2loc.IP2Location_get_country_short(ip);
22 | if (country === '?' || country === '-') return '';
23 | return country;
24 | }
25 |
26 | export function getCountryName(countryCode) {
27 | if (countryCode === 'US') {
28 | return 'United States'; // Instead of United States of America
29 | }
30 | return countries.getName(countryCode, 'en');
31 | }
32 |
--------------------------------------------------------------------------------
/website/js/game.js:
--------------------------------------------------------------------------------
1 | const $ = window.jQuery;
2 |
3 | import similarGamesTemplate from 'pug-loader!../views/_partials/similar-games.pug';
4 |
5 | const mapName = $('#map-name').text();
6 | const gameType = $('#game-type').text();
7 |
8 | window.tryLoadBackground(mapName);
9 |
10 | function loadSimilarGames() {
11 | if (gameType === 'duel' || gameType === 'clanwar') {
12 | const meta = JSON.parse($('#game-meta').text());
13 | const query = `/games/find?gametype=${gameType}&limit=10&players=${meta[0]} ${meta[2]}`;
14 | $.get(`/api${query}`, result => {
15 | $('#similar-games').html(
16 | similarGamesTemplate({
17 | similarGames: result.results,
18 | viewAllLink: query,
19 | })
20 | );
21 | $('#similar-games-parent').css('display', 'block');
22 | disableDefault();
23 | });
24 | }
25 | }
26 | loadSimilarGames();
27 |
--------------------------------------------------------------------------------
/src/tracker/spy.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import moment from 'moment';
3 |
4 | import database from '../util/database';
5 |
6 | let players = {};
7 |
8 | export function addPlayerSpy(name, ip, country, server) {
9 | if (!players[name]) players[name] = {};
10 | players[name][ip] = {
11 | country,
12 | host: server.host,
13 | port: server.port,
14 | time: moment().format('YYYY-MM-DD HH:mm:ss'),
15 | };
16 | }
17 |
18 | export function saveSpy() {
19 | const oldPlayer = players;
20 | players = {};
21 | return _.map(oldPlayer, (player, name) => _.map(player, (info, ip) => database.raw('insert into spy (name, ip, country, lastseen, lshost, lsport) values (?, ?, ?, ?, ?, ?) on conflict (name, ip) do update set country = excluded.country, lastseen = excluded.lastseen, lshost = excluded.lshost, lsport = excluded.lsport', [name, ip, info.country, info.time, info.host, info.port]).then()));
22 | }
23 |
--------------------------------------------------------------------------------
/src/web/server.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import vars from '../../vars.json';
4 | import app from '../util/web';
5 | import { ObjectNotFoundError } from '../util/util';
6 | import { findServer } from '../api/v1/server';
7 | import serverTemplate from '../../website/views/server.pug';
8 |
9 | app.get('/server/:host/:port', (req, res) => {
10 | findServer(req.params.host, req.params.port)
11 | .then(server => {
12 | res.send(
13 | serverTemplate({
14 | server,
15 | vars,
16 | _,
17 | bannerURL: vars.bannerURL,
18 | })
19 | );
20 | })
21 | .catch(ObjectNotFoundError, () => {
22 | res
23 | .status(404)
24 | .render('error', { status: 404, error: 'Server not found.' });
25 | })
26 | .catch(err => {
27 | res.status(500).render('error', { status: 500, error: err.message });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/web/stats.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import moment from 'moment';
3 |
4 | import app from '../util/web';
5 | import database from '../util/database';
6 | import statsTemplate from '../../website/views/stats.pug';
7 |
8 | app.get('/stats', (req, res) => {
9 | const weekAgo = moment()
10 | .endOf('day')
11 | .subtract(7, 'days')
12 | .format('YYYY-MM-DD');
13 | database
14 | .raw(
15 | `SELECT to_char(timestamp, 'MM-DD') AS date, COUNT(*) AS games FROM games WHERE timestamp > '${weekAgo}' GROUP BY date ORDER BY date DESC`
16 | )
17 | .then(result => result.rows)
18 | .then(days => {
19 | const stats = _.orderBy(days, 'date', 'desc');
20 | res.send(
21 | statsTemplate({
22 | stats,
23 | today: moment().format('MM-DD'),
24 | yesterday: moment()
25 | .subtract(1, 'days')
26 | .format('MM-DD'),
27 | })
28 | );
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 | services:
3 | tracker:
4 | build: .
5 | restart: always
6 | ports:
7 | - 8081:8080
8 | environment:
9 | PORT: 8080
10 | BALANCE_PORT: 36581
11 | DATABASE_URL: 'postgres://SauerTracker:postgrespassword@postgres:5432'
12 | REDIS_URL: 'redis://redis'
13 | BASE_ELO: 1200
14 | MASTER_PORT: 28787
15 | MASTER_HOST: master.sauerbraten.org
16 | NODE_ENV: production
17 | entrypoint: './docker-entrypoint.sh'
18 | command: 'start'
19 | depends_on:
20 | - postgres
21 | - redis
22 | postgres:
23 | image: postgres
24 | restart: always
25 | shm_size: 1gb
26 | volumes:
27 | - ./pgdata:/var/lib/postgresql/data
28 | # - ./dump.sql:/docker-entrypoint-initdb.d/init.sql:ro
29 | environment:
30 | POSTGRES_PASSWORD: postgrespassword
31 | POSTGRES_USER: SauerTracker
32 | redis:
33 | image: redis
34 | restart: always
35 |
--------------------------------------------------------------------------------
/src/web/player.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import vars from '../../vars.json';
4 |
5 | import app from '../util/web';
6 | import { ObjectNotFoundError, ObjectBannedError } from '../util/util';
7 | import { getPlayer } from '../api/v1/player';
8 | import playerTemplate from '../../website/views/player.pug';
9 |
10 | app.get('/player/:name', (req, res) => {
11 | getPlayer(req.params.name)
12 | .then(result => {
13 | res.send(playerTemplate(_.assign(result, { _, vars })));
14 | })
15 | .catch(ObjectNotFoundError, () => {
16 | res
17 | .status(404)
18 | .render('error', {
19 | status: 404,
20 | error: 'No player found with this name.',
21 | });
22 | })
23 | .catch(ObjectBannedError, () => {
24 | res
25 | .status(400)
26 | .render('error', { status: 400, error: 'This player name is banned.' });
27 | })
28 | .catch(err => {
29 | res.status(500).render('error', { status: 500, error: err.message });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const glob_entries = require('webpack-glob-entries');
3 |
4 | const serverConfig = {
5 | target: 'node',
6 | entry: {
7 | tracker: './src/index.js',
8 | detectClanwars: './src/scripts/detect-clanwars.js',
9 | recalculateElo: './src/scripts/recalculate-elo.js',
10 | },
11 | output: {
12 | path: path.resolve(__dirname, 'dist'),
13 | filename: '[name].js',
14 | },
15 | externals: {
16 | knex: 'commonjs knex',
17 | },
18 | module: {
19 | rules: [{ test: /\.pug$/, use: 'pug-loader' }],
20 | },
21 | };
22 |
23 | const websiteConfig = {
24 | entry: glob_entries('./website/js/[^_]*.js'),
25 | output: {
26 | path: path.resolve(__dirname, 'assets/js'),
27 | filename: '[name].js',
28 | },
29 | module: {
30 | rules: [{ test: /\.pug/, use: 'pug-loader' }],
31 | rules: [
32 | { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
33 | ],
34 | },
35 | };
36 |
37 | module.exports = [serverConfig, websiteConfig];
38 |
--------------------------------------------------------------------------------
/src/util/config.js:
--------------------------------------------------------------------------------
1 | export function getPort() {
2 | const variable = parseInt(process.env.PORT);
3 | return variable ? variable : 8080;
4 | }
5 |
6 | export function getBalancePort() {
7 | const variable = parseInt(process.env.BALANCE_PORT);
8 | return variable ? variable : 36581;
9 | }
10 |
11 | export function getDatabaseURL() {
12 | const variable = process.env.DATABASE_URL;
13 | return variable ? variable : 'postgres://localhost:5432';
14 | }
15 |
16 | export function getRedisURL() {
17 | const variable = process.env.REDIS_URL;
18 | return variable ? variable : 'redis://localhost:6379';
19 | }
20 |
21 | export function getBaseElo() {
22 | const variable = parseInt(process.env.BASE_ELO);
23 | return variable ? variable : 1200;
24 | }
25 |
26 | export function getMasterPort() {
27 | const variable = parseInt(process.env.MASTER_PORT);
28 | return variable ? variable : 28787;
29 | }
30 |
31 | export function getMasterHost() {
32 | const variable = process.env.MASTER_HOST;
33 | return variable ? variable : 'sauerbraten.org';
34 | }
35 |
--------------------------------------------------------------------------------
/website/js/server.js:
--------------------------------------------------------------------------------
1 | import { loadCharts } from './_server-charts';
2 | import url from 'url';
3 | import _ from 'lodash';
4 | import vars from '../../vars.json';
5 | import gameTemplate from 'pug-loader!../views/_partials/game.pug';
6 |
7 | const $ = window.jQuery;
8 |
9 | const urlPath = url.parse(window.location.href).pathname.split('/');
10 | const host = urlPath[2];
11 | const port = parseInt(urlPath[3], 10);
12 |
13 | const $banner = $('#banner');
14 | const bannerURL = $banner.length ? $banner.attr('src') : null;
15 |
16 | function updateGame() {
17 | $.get(`/api/server/${host}/${port}`, result => {
18 | render(result);
19 | });
20 | }
21 | setInterval(updateGame, 5000);
22 |
23 | function updateBanner() {
24 | $banner.attr('src', `${bannerURL}#${new Date().getTime()}`);
25 | }
26 | if (bannerURL) setInterval(updateBanner, 10000);
27 |
28 | window.tryLoadBackground($('#map-name').text());
29 |
30 | loadCharts(host, port);
31 |
32 | function render(game) {
33 | window.tryLoadBackground(game.mapName);
34 | $('#game').html(gameTemplate({ server: game, vars, _ }));
35 | }
36 |
--------------------------------------------------------------------------------
/website/views/game.pug:
--------------------------------------------------------------------------------
1 | extends ./_layout.pug
2 |
3 | block vars
4 | - section = "games";
5 | - title = server.description;
6 |
7 | block content
8 | .row.main-container
9 | ul.breadcrumbs
10 | li Games
11 | li.current= id
12 | .medium-12.columns
13 | #game
14 | include ./_partials/game.pug
15 | #similar-games-parent(style="display: none;")
16 | label.big Similar Games
17 | span#game-meta(style="display: none;")= JSON.stringify(server.meta)
18 | span#game-type(style="display: none;")= server.gameType
19 | .row
20 | .large-6.columns
21 | #similar-games
22 |
23 | .reveal#connect-info(data-reveal)
24 | | Copy the following line, paste it in the game, and press Enter to connect to the server:
25 | br
26 | br
27 | input#connect-command(type="text", value="/connect", onfocus="$(this).select()")
28 |
29 | block scripts
30 | script(src="/js/game.js")
31 |
--------------------------------------------------------------------------------
/migrations/20171211131355_add_constraints.js:
--------------------------------------------------------------------------------
1 |
2 | exports.up = function(knex, Promise) {
3 | return Promise.all([
4 | knex.raw('DELETE FROM spy a USING spy b WHERE a.id < b.id AND a.name = b.name AND a.ip = b.ip'),
5 | knex.raw('DELETE FROM bans a USING bans b WHERE a.id < b.id AND a.name = b.name AND a.ip = b.ip'),
6 | knex.raw('DELETE FROM servers a USING servers b WHERE a.id < b.id AND a.host = b.host AND a.port = b.port')
7 | ]).then(() => Promise.all([
8 | knex.schema.table('spy', function (table) {
9 | table.unique(['name', 'ip']);
10 | }),
11 | knex.schema.table('bans', function (table) {
12 | table.unique(['name', 'ip']);
13 | }),
14 | knex.schema.table('servers', function (table) {
15 | table.unique(['host', 'port']);
16 | })
17 | ]));
18 | };
19 |
20 | exports.down = function(knex, Promise) {
21 | return Promise.all([
22 | knex.schema.table('spy', function (table) {
23 | table.dropUnique(['name', 'ip']);
24 | }),
25 | knex.schema.table('bans', function (table) {
26 | table.dropUnique(['name', 'ip']);
27 | }),
28 | knex.schema.table('servers', function (table) {
29 | table.dropUnique(['host', 'port']);
30 | })
31 | ]);
32 | };
33 |
--------------------------------------------------------------------------------
/src/api/v2/server.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import app from '../../util/web';
4 | import { ObjectNotFoundError } from '../../util/util';
5 |
6 | import database from '../../util/database';
7 | import serverManager from '../../tracker/server-manager';
8 |
9 | export function findServer(host, port) {
10 | port = parseInt(port, 10);
11 | let server = serverManager.find(host, port);
12 | if (server) {
13 | server = server.serialize(true);
14 |
15 | if (server.teams) server.teams = _.map(server.teams, (score, name) => ({ name, score }));
16 |
17 | return database('serverranks').where({ host, port }).select('count', 'rank').then((result) => {
18 | if (result.length) {
19 | server.totalGames = result[0].count;
20 | server.rank = result[0].rank;
21 | }
22 | return server;
23 | });
24 | }
25 | return Promise.reject(new ObjectNotFoundError());
26 | }
27 |
28 | app.get('/api/v2/server/:host/:port', (req, res) => {
29 | findServer(req.params.host, req.params.port)
30 | .then((server) => { res.send(server); })
31 | .catch(ObjectNotFoundError, () => { res.status(404).send({ error: 'Server not found.' }); })
32 | .catch((err) => { res.status(500).send({ error: err }); });
33 | });
34 |
--------------------------------------------------------------------------------
/src/util/protocol.js:
--------------------------------------------------------------------------------
1 | export const gameModes = [
2 | 'ffa',
3 | 'coop_edit',
4 | 'teamplay',
5 | 'instagib',
6 | 'insta_team',
7 | 'efficiency',
8 | 'effic_team',
9 | 'tactics',
10 | 'tac_team',
11 | 'capture',
12 | 'regen_capture',
13 | 'ctf',
14 | 'insta_ctf',
15 | 'protect',
16 | 'insta_protect',
17 | 'hold',
18 | 'insta_hold',
19 | 'effic_ctf',
20 | 'effic_protect',
21 | 'effic_hold',
22 | 'collect',
23 | 'insta_collect',
24 | 'effic_collect',
25 | ];
26 |
27 | export const masterModes = [
28 | 'auth', // -1
29 | 'open', // 0
30 | 'veto', // 1
31 | 'locked', // 2
32 | 'private', // 3
33 | 'password', // 4
34 | ];
35 |
36 | /**
37 | * Maps game mode number to name, according to protocol 259.
38 | * @param {number} code - The mode number.
39 | * @returns {string} name - Name of the mode.
40 | */
41 | export function getGameMode(code) {
42 | return gameModes[code] || 'unknown';
43 | }
44 |
45 | /**
46 | * Maps mastermode number to name, according to protocol 259.
47 | * @param {number} code - The mastermode number.
48 | * @returns {string} name - Name of the mastermode.
49 | */
50 | export function getMasterMode(code) {
51 | return masterModes[code + 1] || 'unknown';
52 | }
53 |
--------------------------------------------------------------------------------
/website/views/_partials/similar-games.pug:
--------------------------------------------------------------------------------
1 | table.scroll#search-results(width="50%")
2 | thead
3 | tr
4 | td(width="40%") Mode / Map
5 | td.text-center(width="60%") Result
6 | tbody
7 | for game in similarGames
8 | tr.clickable(onclick="if (window.history) { history.pushState({}, window.location.href); } window.location.replace('/game/"+game.id+"')")
9 | td: a.disable-default(href="/game/"+game.id)= game.gamemode + " " + game.map
10 | td: a.disable-default(href="/game/"+game.id)
11 | .row
12 | .small-6.columns(style="text-align: right; padding-right: 10;")
13 | = game.meta[2]+" "
14 | span.label(class=(game.draw? "warning": "success"), style="cursor: pointer")
15 | = game.meta[3]
16 | .small-6.columns(style="text-align: left; padding-right: 10;")
17 | span.label(class=(game.draw? "warning": "alert"), style="cursor: pointer")
18 | = game.meta[1]
19 | = " "+game.meta[0]
20 | tr: td(colspan=2): a(href=viewAllLink) View all...
21 |
--------------------------------------------------------------------------------
/website/views/error.pug:
--------------------------------------------------------------------------------
1 | extends ./_layout.pug
2 |
3 | block vars
4 | - section = "";
5 | - title = "Error "+status;
6 |
7 | block content
8 | .row.main-container
9 | ul.breadcrumbs
10 | li.current= "Error "+status
11 | .medium-12.columns
12 | if status===404
13 | h3 Error 404. Page not found.
14 | if error
15 | = error
16 | else
17 | | If you typed the address manually, check your spelling.
18 | br
19 | | If this is a server's page, the server might be down at the moment. Try again at a later time.
20 | else if status===500
21 | h3 Error 500. Internal server error.
22 | | An internal error occurred. Please report this using the links in the footer below.
23 | if error
24 | br
25 | | Error message:
26 | br
27 | br
28 | div(style="margin-left: 2em;") !{error.stack? error.stack.replace(/\n/g, "
"): error}
29 | else
30 | h3= "Error "+status+"."
31 | if error
32 | = error
33 |
--------------------------------------------------------------------------------
/src/api/v1/servers.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | import _ from 'lodash';
3 |
4 | import app from '../../util/web';
5 | import cache from '../../util/cache';
6 | import serverManager from '../../tracker/server-manager';
7 | import redis from '../../util/redis';
8 |
9 | app.get('/api/servers', (req, res) => {
10 | redis.getAsync('server-list')
11 | .then((list) => { res.setHeader('Content-Type', 'application/json'); res.send(list); })
12 | .catch((err) => { res.status(500).send({ error: err }); });
13 | });
14 |
15 | cache.set('server-countries', 5000, () => {
16 | const res = {};
17 | res.servers = _.countBy(serverManager.list, 'countryName');
18 | res.players = {};
19 | res.links = {};
20 |
21 | _.each(serverManager.list, (server) => {
22 | _.each(_.countBy(server.game.players, 'countryName'), (val, ind) => {
23 | if (ind === 'Unknown') return;
24 |
25 | res.players[ind] = res.players[ind] ? res.players[ind] + val : val;
26 |
27 | if (!res.links[server.countryName]) res.links[server.countryName] = {};
28 | res.links[server.countryName][ind] = true;
29 | });
30 | });
31 |
32 | return Promise.resolve(res);
33 | });
34 |
35 | app.get('/api/server-countries', (req, res) => {
36 | cache.get('server-countries')
37 | .then((list) => { res.send(list); })
38 | .catch((err) => { res.status(500).send({ error: err }); });
39 | });
40 |
--------------------------------------------------------------------------------
/migrations/20180214041134_update_players_schema_2.js:
--------------------------------------------------------------------------------
1 |
2 | exports.up = function(knex, Promise) {
3 | return knex.raw(`Update players SET
4 | instastats = json_build_array(
5 | instastats::json->0,
6 | instastats::json->1,
7 | instastats::json->2,
8 | instastats::json->3,
9 | (instastats::json->5)::TEXT::NUMERIC * (instastats::json->0)::TEXT::NUMERIC
10 | ), efficstats = json_build_array(
11 | efficstats::jsonb->0,
12 | efficstats::json->1,
13 | efficstats::json->2,
14 | efficstats::json->3,
15 | (efficstats::json->5)::TEXT::NUMERIC * (efficstats::json->0)::TEXT::NUMERIC
16 | )`);
17 | };
18 |
19 | exports.down = function(knex, Promise) {
20 | return knex.raw(`Update players SET
21 | instastats = json_build_array(
22 | instastats::json->0,
23 | instastats::json->1,
24 | instastats::json->2,
25 | instastats::json->3,
26 | (instastats::json->0)::TEXT::NUMERIC / (instastats::jsonb->2)::TEXT::NUMERIC,
27 | (instastats::json->5)::TEXT::NUMERIC / (instastats::jsonb->0)::TEXT::NUMERIC
28 | ), efficstats = json_build_array(
29 | efficstats::json->0,
30 | efficstats::json->1,
31 | efficstats::json->2,
32 | efficstats::json->3,
33 | (efficstats::json->0)::TEXT::NUMERIC / (efficstats::jsonb->2)::TEXT::NUMERIC,
34 | (efficstats::json->5)::TEXT::NUMERIC / (efficstats::json->0)::TEXT::NUMERIC
35 | )`);
36 | };
37 |
--------------------------------------------------------------------------------
/website/views/metrics.pug:
--------------------------------------------------------------------------------
1 | extends ./_layout.pug
2 |
3 | block vars
4 | - section = "home";
5 | - title = "Metrics";
6 |
7 | block content
8 | .row.main-container
9 | ul.breadcrumbs
10 | li.current= title
11 | .medium-12.columns.end
12 | label.big Number of servers: #{Object.keys(metrics).length}
13 | br
14 | label.big Server poll stats:
15 | table
16 | thead
17 | tr
18 | td(width="200px") Server
19 | td(width="200px", style="text-align: right;") Total Polls
20 | td(width="200px", style="text-align: right;") Total Replies
21 | td(width="200px", style="text-align: right;") Loss
22 | tbody
23 | for metric, key in metrics
24 | tr
25 | td #{key}
26 | td(style="text-align: right;") #{metric.polls || 0}
27 | td(style="text-align: right;") #{metric.replies || 0}
28 | if metric.polls > 0
29 | td(style="text-align: right;") #{Math.floor(100 - 100 * ((metric.replies || 0) / metric.polls))}%
30 | else
31 | td(style="text-align: right;") 100%
32 |
--------------------------------------------------------------------------------
/src/util/metrics.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import Promise from 'bluebird';
3 |
4 | import redis from './redis';
5 |
6 | /**
7 | * Server has been polled.
8 | * @param {string} host - Server's IP address.
9 | * @param {number} port - Server's port number.
10 | */
11 | export function serverPolled(host, port) {
12 | return redis.hincrbyAsync('servers-polled', `${host}:${port}`, 1);
13 | }
14 |
15 | /**
16 | * Server has replied.
17 | * @param {string} host - Server's IP address.
18 | * @param {number} port - Server's port number.
19 | */
20 | export function serverReplied(host, port) {
21 | return redis.hincrbyAsync('servers-replied', `${host}:${port}`, 1);
22 | }
23 |
24 | /**
25 | * Get all stats.
26 | * @returns {object} An object whose keys correspond to `host:port` values of servers,
27 | * and whose values are objects containing the properties `polls` and `replies`,
28 | * representing the total number of times the server was polled and the total number of
29 | * times it replied, respectively.
30 | */
31 | export function getAllMetrics() {
32 | return Promise.join(
33 | redis.hgetallAsync('servers-polled'), redis.hgetallAsync('servers-replied'),
34 | (serversPolled, serversReplied) => _.merge(
35 | {},
36 | _.mapValues(serversPolled, polls => ({ polls: Number(polls) })),
37 | _.mapValues(serversReplied, replies => ({ replies: Number(replies) })),
38 | ),
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/src/api/team-balance.js:
--------------------------------------------------------------------------------
1 | import dgram from 'dgram';
2 |
3 | import { logInfo, logError } from '../util/util';
4 | import { getBalancePort } from '../util/config';
5 | import Packet from '../util/packet';
6 |
7 | export default function startTeamBalanceServer() {
8 | const server = dgram.createSocket('udp4');
9 |
10 | server.on('error', err => {
11 | logError(`server error:\n${err.stack}`);
12 | server.close();
13 | });
14 |
15 | server.on('message', (data, rinfo) => {
16 | const st = new Packet(data, 0);
17 | let s = st.getString();
18 | const names = [];
19 | let mode;
20 | let map;
21 | while (s) {
22 | names.push(s);
23 | s = st.getString();
24 | }
25 | s = st.getString();
26 | if (s) mode = s;
27 | s = st.getString();
28 | if (s) map = s;
29 |
30 | makeTeams(names, mode, map).then(teams => {
31 | // eslint-disable-next-line new-cap
32 | const buf = new Buffer.alloc(1024);
33 | const p = new Packet(buf);
34 |
35 | _.each(teams, team => {
36 | _.each(team, player => {
37 | p.putInt(names.indexOf(player));
38 | });
39 | });
40 |
41 | server.send(buf, 0, p.offset, rinfo.port, rinfo.address);
42 | });
43 | });
44 |
45 | server.on('listening', () => {
46 | const address = server.address();
47 | logInfo(`Team balance server listening on port ${address.port}`);
48 | });
49 |
50 | server.bind(getBalancePort());
51 | }
52 |
--------------------------------------------------------------------------------
/src/util/web.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import http from 'http';
3 | import bodyParser from 'body-parser';
4 |
5 | import { logInfo } from '../util/util';
6 | import { getPort } from '../util/config';
7 |
8 | const app = express();
9 | export default app;
10 |
11 | app.set('trust proxy', 'loopback');
12 |
13 | app.use(bodyParser.json());
14 | app.use(bodyParser.urlencoded({ extended: true }));
15 |
16 | if (process.env.NODE_ENV !== 'production') app.locals.pretty = true;
17 |
18 | app.use((req, res, next) => {
19 | // If using an old domain, redirect to "sauertracker.net"
20 | if (
21 | req.get('host') === 'uk.cube2.org' ||
22 | req.get('host') === 'tracker.impressivesquad.eu'
23 | ) {
24 | res.redirect(`${req.protocol}://sauertracker.net${req.originalUrl}`);
25 | return;
26 | }
27 |
28 | // Enable cross-origin on all routes
29 | res.header('Access-Control-Allow-Origin', '*');
30 | res.header(
31 | 'Access-Control-Allow-Headers',
32 | 'Origin, X-Requested-With, Content-Type, Accept'
33 | );
34 |
35 | // Disable caching on /api/ routes (fixes bug on Dolphin browser)
36 | if (req.path.indexOf('/api/') === 0) {
37 | res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
38 | res.setHeader('Pragma', 'no-cache');
39 | res.setHeader('Expires', '0');
40 | }
41 |
42 | next();
43 | });
44 |
45 | app.use('/', express.static('./assets', { maxAge: 24 * 60 * 60 * 1000 }));
46 |
47 | http.createServer(app.handle.bind(app)).listen(getPort(), () => {
48 | logInfo(`Server listening on port ${getPort()}`);
49 | });
50 |
--------------------------------------------------------------------------------
/src/scripts/detect-clanwars.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/first */
2 | require('source-map-support').install();
3 |
4 | import _ from 'lodash';
5 |
6 | import database from '../util/database';
7 | import { getGameType } from '../tracker/game';
8 |
9 | let count = 0;
10 |
11 | database.raw(`
12 | SELECT games.id,
13 | games.gamemode,
14 | games.gametype,
15 | (SELECT array_to_json(array_agg(row(scores.team, scores.score))) FROM scores WHERE scores.game = games.id) teams,
16 | (SELECT array_to_json(array_agg(row(stats.name, stats.team, stats.frags)))app FROM stats WHERE stats.game = games.id AND stats.state != 5) players
17 |
18 | FROM games
19 |
20 | WHERE games.gametype = 'mix' OR
21 | games.gametype = 'clanwar'
22 | `)
23 | // eslint-disable-next-line array-callback-return,consistent-return
24 | .then(result => result.rows).map((row) => {
25 | const players = _.map(row.players, player => ({
26 | name: player.f1,
27 | team: player.f2,
28 | frags: player.f3,
29 | }));
30 | const teams = {};
31 | _.each(row.teams, (team) => {
32 | teams[team.f1] = team.f2;
33 | });
34 | const g = {
35 | masterMode: 'locked', teams, players, gameMode: row.gamemode,
36 | };
37 | const gameType = getGameType(g);
38 | database.raw();
39 | if (gameType[0] !== row.gametype) {
40 | count++;
41 | return database('games').where('id', row.id).update({ gametype: gameType[0], meta: JSON.stringify(gameType[1]) }).then();
42 | }
43 | }).then(() => {
44 | console.log(`Updated ${count} games`); // eslint-disable-line no-console
45 | process.exit();
46 | });
47 |
--------------------------------------------------------------------------------
/migrations/20161202143509_update_mode_names.js:
--------------------------------------------------------------------------------
1 |
2 | exports.up = function(knex, Promise) {
3 | return knex.raw(`
4 | UPDATE games SET
5 | gamemode = c.new_name
6 | FROM (values
7 | ('coop', 'coop_edit'),
8 | ('insta', 'instagib'),
9 | ('instateam', 'insta_team'),
10 | ('effic', 'efficiency'),
11 | ('efficteam', 'effic_team'),
12 | ('tac', 'tactics'),
13 | ('tacteam', 'tac_team'),
14 | ('rcapture', 'regen_capture'),
15 | ('ictf', 'insta_ctf'),
16 | ('iprotect', 'insta_protect'),
17 | ('ihold', 'insta_hold'),
18 | ('ectf', 'effic_ctf'),
19 | ('eprotect', 'effic_protect'),
20 | ('ehold', 'effic_hold'),
21 | ('icollect', 'insta_collect'),
22 | ('ecollect', 'effic_collect')
23 | ) AS c(old_name, new_name)
24 | WHERE c.old_name = games.gamemode
25 | `);
26 | };
27 |
28 | exports.down = function(knex, Promise) {
29 | return knex.raw(`
30 | UPDATE games SET
31 | gamemode = c.new_name
32 | FROM (values
33 | ('coop_edit', 'coop'),
34 | ('instagib', 'insta'),
35 | ('insta_team', 'instateam'),
36 | ('efficiency', 'effic'),
37 | ('effic_team', 'efficteam'),
38 | ('tactics', 'tac'),
39 | ('tac_team', 'tacteam'),
40 | ('regen_capture', 'rcapture'),
41 | ('insta_ctf', 'ictf'),
42 | ('insta_protect', 'iprotect'),
43 | ('insta_hold', 'ihold'),
44 | ('effic_ctf', 'ectf'),
45 | ('effic_protect', 'eprotect'),
46 | ('effic_hold', 'ehold'),
47 | ('insta_collect', 'icollect'),
48 | ('effic_collect', 'ecollect')
49 | ) AS c(old_name, new_name)
50 | WHERE c.old_name = games.gamemode
51 | `);
52 | };
53 |
--------------------------------------------------------------------------------
/website/js/players.js:
--------------------------------------------------------------------------------
1 | import NProgress from 'nprogress';
2 | import _ from 'lodash';
3 |
4 | const $ = window.jQuery;
5 |
6 | import searchResultsTemplate from 'pug-loader!../views/_partials/player-search-results.pug';
7 |
8 | const originalURL =
9 | window.location.pathname + window.location.search + window.location.hash;
10 |
11 | function loadPage(url, name) {
12 | NProgress.start();
13 | $.get(`/api${url}`)
14 | .success(result => {
15 | $('#search-result-container').html(
16 | searchResultsTemplate({ results: result.results, _ })
17 | );
18 | })
19 | .fail(() => {
20 | $('#search-result-container').html('Error loading search results.');
21 | })
22 | .always(() => {
23 | $('#name').val(name);
24 | NProgress.done();
25 | });
26 | }
27 |
28 | $('#search-form').on('submit', function(event) {
29 | event.preventDefault();
30 | const url = `/players/find?${$(this).serialize()}`;
31 | const name = $('#name').val();
32 | loadPage(url, name);
33 | window.history.pushState({ url, name }, window.title, url);
34 | });
35 |
36 | $(window).bind('popstate', event => {
37 | const { state } = event.originalEvent;
38 | if (!state) {
39 | if (originalURL === '/players') window.location.reload();
40 | else loadPage(originalURL);
41 | } else loadPage(state.url, state.name);
42 | });
43 |
44 | window.selectCategory = function(category) {
45 | $('.category-body').hide();
46 | $(`#top-${category}`).show();
47 | $('.category-title').removeClass('inverted');
48 | $(`#ct-${category}`).addClass('inverted');
49 | };
50 |
51 | window.selectCategory('monthly');
52 |
--------------------------------------------------------------------------------
/website/views/server.pug:
--------------------------------------------------------------------------------
1 | extends ./_layout.pug
2 |
3 | block vars
4 | - section = "servers";
5 | - title = server.description;
6 |
7 | block content
8 | .row.main-container
9 | ul.breadcrumbs
10 | li Servers
11 | li.current #{server.description}
12 | .medium-12.columns
13 | #game
14 | include ./_partials/game.pug
15 | if bannerURL
16 | br
17 | a(href="/banners?type=server&host="+server.host+"&port="+server.port)
18 | img#banner(src=bannerURL+"server?theme=default&host="+server.host+"&port="+server.port)
19 | if !server.info.banned && !server.zombie
20 | .row.columns.client-side(style="margin-top: 1em")
21 | label.big Server activity for today (avg # of players, time is in UTC):
22 | div(style="width: 100%; margin-right: 50px;")
23 | canvas#server-activity-day(style="width: 100%; height: 300px;")
24 | .row.columns.client-side
25 | label.big Server activity for the last 15 days (# of games):
26 | div(style="width: 100%; margin-right: 50px;")
27 | canvas#server-activity-month(style="width: 100%; height: 300px;")
28 |
29 | .reveal#connect-info(data-reveal)
30 | | Copy the following line, paste it in the game, and press Enter to connect to the server:
31 | br
32 | br
33 | input#connect-command(type="text", value="/connect", onfocus="$(this).select()")
34 |
35 | block scripts
36 | script(src="/js/server.js")
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SauerTracker",
3 | "version": "4.0.0",
4 | "description": "Server and stats tracker for Cube 2: Sauerbraten.",
5 | "main": "index.js",
6 | "repository": "https://github.com/AngrySnout/SauerTracker.git",
7 | "author": "AngrySnout",
8 | "license": "MIT",
9 | "scripts": {
10 | "build": "webpack-cli --mode production",
11 | "buildDev": "webpack-cli --mode development",
12 | "migrate": "knex migrate:latest",
13 | "start": "node dist/tracker.js",
14 | "detectClanwars": "node dist/detectClanwars.js",
15 | "recalculateElo": "node dist/recalculateElo.js"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.8.4",
19 | "@babel/node": "^7.8.4",
20 | "@babel/preset-env": "^7.8.4",
21 | "css-loader": "^3.4.2",
22 | "imports-loader": "^0.8.0",
23 | "node-sass": "^4.13.1",
24 | "pug-loader": "^2.4.0",
25 | "sass-loader": "^8.0.2",
26 | "style-loader": "^1.1.3",
27 | "webpack": "^4.41.5",
28 | "webpack-cli": "^3.3.10",
29 | "webpack-glob-entries": "^1.0.1"
30 | },
31 | "dependencies": {
32 | "bluebird": "^3.7.2",
33 | "body-parser": "^1.19.0",
34 | "chart.js": "^2.9.3",
35 | "codemirror": "^5.51.0",
36 | "express": "^4.17.1",
37 | "font-awesome": "^4.7.0",
38 | "fuzzaldrin": "^2.1.0",
39 | "handlebars": "^4.7.3",
40 | "i18n-iso-countries": "^4.3.1",
41 | "ip2location-nodejs": "^8.3.0",
42 | "knex": "^0.21.1",
43 | "lodash": "^4.17.15",
44 | "moment": "^2.24.0",
45 | "nprogress": "^0.2.0",
46 | "pg": "^8.0.3",
47 | "pug": "^2.0.4",
48 | "pug-runtime": "^2.0.5",
49 | "redis": "^3.0.2",
50 | "winston": "^3.2.1",
51 | "winston-daily-rotate-file": "^4.4.2"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/website/views/_partials/player-search-results.pug:
--------------------------------------------------------------------------------
1 | if results.length
2 | table.scroll#search-results(width="100%")
3 | thead
4 | tr
5 | td(width="19%") Name
6 | td(width="9%") Frags
7 | td(width="9%") Flags
8 | td(width="9%") Deaths
9 | td(width="9%") TKs
10 | td(width="9%") K/D
11 | td(width="9%") Accuracy
12 | td(width="9%") Elo
13 | td(width="18%") Country
14 | tbody
15 | for player in results
16 | tr.unclickable
17 | td
18 | a(href=("/player/"+encodeURIComponent(player.name)))= player.name
19 | if player.online
20 | |
21 | span.label.success online
22 | td= player.frags
23 | td= player.flags
24 | td= player.deaths
25 | td= player.tks
26 | td= player.kpd
27 | td= _.padStart(parseInt(player.acc).toString(), 2, "0")+"%"
28 | td= player.elo
29 | td
30 | if player.country
31 | img.flag(src=("/images/flags/"+player.country+".png"))
32 | |
33 | a.no-color(href="/players/find?country="+player.country)= player.countryName
34 | else
35 | | Unknown
36 |
37 | if (results.length == 200)
38 | | Results are too big to show. You should narrow down your search query.
39 |
40 | else
41 | | No results.
42 |
--------------------------------------------------------------------------------
/src/api/v2/clans.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable guard-for-in,no-restricted-syntax */
2 | import _ from 'lodash';
3 | import Promise from 'bluebird';
4 |
5 | import vars from '../../../vars.json';
6 |
7 | import app from '../../util/web';
8 | import cache from '../../util/cache';
9 | import { logError, round2 } from '../../util/util';
10 | import redis from '../../util/redis';
11 |
12 | cache.set('clans-v2', 10 * 60 * 1000, () => Promise.join(
13 | redis.hgetallAsync('clan-games'), redis.hgetallAsync('clan-wins'), redis.hgetallAsync('clan-losses'),
14 | (games, wins, losses) => {
15 | games = _.mapValues(games, Number);
16 | wins = _.mapValues(wins, Number);
17 | losses = _.mapValues(losses, Number);
18 | const clns = _.orderBy(_.map(vars.clans, (clan) => {
19 | if (!wins[clan.tag]) wins[clan.tag] = 0;
20 | if (!losses[clan.tag]) losses[clan.tag] = 0;
21 | const draws = ((games[clan.tag] || 0) - ((wins[clan.tag] || 0) + (losses[clan.tag] || 0)));
22 | const rate = (games[clan.tag] ? (wins[clan.tag] + draws / 2) / games[clan.tag] : 0);
23 | return {
24 | tag: clan.tag,
25 | title: clan.title,
26 | website: clan.website,
27 | wins: wins[clan.tag],
28 | losses: losses[clan.tag],
29 | ties: draws,
30 | rate: round2(rate),
31 | points: round2((wins[clan.tag] + draws / 2) * rate),
32 | };
33 | }), 'points', 'desc');
34 | let rank = 1;
35 | _.each(clns, (clan) => {
36 | clan.rank = rank++;
37 | });
38 | return clns;
39 | },
40 | ));
41 |
42 | export function getClans() {
43 | return cache.get('clans-v2');
44 | }
45 |
46 | app.get('/api/v2/clans', (req, res) => {
47 | getClans().then((clans) => {
48 | res.send({ clans });
49 | }).catch((err) => {
50 | logError(err);
51 | res.status(500).send({ error: err.message });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/src/util/cache.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 |
3 | import redis from './redis';
4 | import { logError } from './util';
5 |
6 | class CacheManager {
7 | constructor() {
8 | this.entries = {};
9 | }
10 |
11 | /**
12 | * Add a cache function.
13 | * @param {string} id - A unique name for the function.
14 | * @param {string} maxage - Approximately, how often, in milliseconds,
15 | * should the stored value be purged.
16 | * @param {function} func - A function that returns a promise which resolves
17 | * to the value that should be stored.
18 | */
19 | set(id, maxage, func) {
20 | this.entries[id] = { maxage: maxage / 1000, func };
21 | }
22 |
23 | /**
24 | * Get a value from cache. If the value stored has expired the
25 | * generator function will be called again.
26 | * @param id - {string} The name of the function to call.
27 | * @returns {Promise} Rejects if the function is not set.
28 | * Resolves with the value returned by the generator function.
29 | */
30 | get(id) {
31 | const self = this;
32 | return new Promise((resolve, reject) => {
33 | if (!self.entries[id]) {
34 | // eslint-disable-next-line prefer-promise-reject-errors
35 | reject('Cache entry not set');
36 | return;
37 | }
38 | const key = `cache-${id}`;
39 | redis.getAsync(key)
40 | .then((reply) => {
41 | if (!reply) {
42 | self.entries[id].func().then((res) => {
43 | redis.setAsync(key, JSON.stringify(res), 'EX', self.entries[id].maxage).then(() => {
44 | resolve(res);
45 | });
46 | }).catch((err) => {
47 | logError(err);
48 | resolve();
49 | });
50 | } else resolve(JSON.parse(reply.toString()));
51 | });
52 | });
53 | }
54 | }
55 |
56 | const cache = new CacheManager();
57 | export default cache;
58 |
--------------------------------------------------------------------------------
/website/styles/_nprogress.scss:
--------------------------------------------------------------------------------
1 | $progress-bar-color: #dd7c22;
2 |
3 | /* Make clicks pass-through */
4 | #nprogress {
5 | pointer-events: none;
6 | }
7 |
8 | #nprogress .bar {
9 | background: $progress-bar-color;
10 |
11 | position: fixed;
12 | z-index: 1031;
13 | top: 0;
14 | left: 0;
15 |
16 | width: 100%;
17 | height: 2px;
18 | }
19 |
20 | /* Fancy blur effect */
21 | #nprogress .peg {
22 | display: block;
23 | position: absolute;
24 | right: 0px;
25 | width: 100px;
26 | height: 100%;
27 | box-shadow: 0 0 10px $progress-bar-color, 0 0 5px $progress-bar-color;
28 | opacity: 1.0;
29 |
30 | -webkit-transform: rotate(3deg) translate(0px, -4px);
31 | -ms-transform: rotate(3deg) translate(0px, -4px);
32 | transform: rotate(3deg) translate(0px, -4px);
33 | }
34 |
35 | /* Remove these to get rid of the spinner */
36 | #nprogress .spinner {
37 | display: block;
38 | position: fixed;
39 | z-index: 1031;
40 | top: 15px;
41 | right: 15px;
42 | }
43 |
44 | #nprogress .spinner-icon {
45 | width: 18px;
46 | height: 18px;
47 | box-sizing: border-box;
48 |
49 | border: solid 2px transparent;
50 | border-top-color: $progress-bar-color;
51 | border-left-color: $progress-bar-color;
52 | border-radius: 50%;
53 |
54 | -webkit-animation: nprogress-spinner 400ms linear infinite;
55 | animation: nprogress-spinner 400ms linear infinite;
56 | }
57 |
58 | .nprogress-custom-parent {
59 | overflow: hidden;
60 | position: relative;
61 | }
62 |
63 | .nprogress-custom-parent #nprogress .spinner,
64 | .nprogress-custom-parent #nprogress .bar {
65 | position: absolute;
66 | }
67 |
68 | @-webkit-keyframes nprogress-spinner {
69 | 0% { -webkit-transform: rotate(0deg); }
70 | 100% { -webkit-transform: rotate(360deg); }
71 | }
72 | @keyframes nprogress-spinner {
73 | 0% { transform: rotate(0deg); }
74 | 100% { transform: rotate(360deg); }
75 | }
76 |
--------------------------------------------------------------------------------
/src/api/v1/clan.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import Promise from 'bluebird';
3 |
4 | import vars from '../../../vars.json';
5 |
6 | import app from '../../util/web';
7 | import cache from '../../util/cache';
8 | import { logError, ObjectNotFoundError, escapePostgresLike } from '../../util/util';
9 | import database from '../../util/database';
10 |
11 | function getLatestGames(clan) {
12 | return database('games').where('meta', 'like', `%${escapePostgresLike(clan.replace(/"/g, '\\"'))}%`).where({ gametype: 'clanwar' }).orderBy('id', 'desc')
13 | .limit(10)
14 | .then((rows) => {
15 | _.each(rows, (game) => {
16 | if (game.meta) {
17 | try {
18 | game.meta = JSON.parse(game.meta);
19 | } catch (e) {
20 | game.meta = [];
21 | }
22 | if (game.meta[1] === game.meta[3]) game.draw = true;
23 | } else game.meta = [];
24 | });
25 |
26 | return rows;
27 | });
28 | }
29 |
30 | function getLatestMembers(clan) {
31 | return database('spy').where('name', 'ilike', `%${escapePostgresLike(clan)}%`).max('lastseen as lastseen').select('name')
32 | .groupBy('name')
33 | .orderBy('lastseen', 'desc')
34 | .limit(9);
35 | }
36 |
37 | export function getClan(name) {
38 | return Promise.all([cache.get('clans'), getLatestGames(name), getLatestMembers(name)])
39 | .spread((clans, latestGames, latestMembers) => {
40 | const clan = _.find(clans, { name });
41 | if (!clan) throw new ObjectNotFoundError();
42 |
43 | const info = _.find(vars.clans, { tag: name });
44 | return {
45 | clan, info, games: latestGames, members: latestMembers,
46 | };
47 | });
48 | }
49 |
50 | app.get('/api/clan/:name', (req, res) => {
51 | getClan(req.params.name)
52 | .then((result) => { res.send(result); })
53 | .catch(ObjectNotFoundError, () => { res.status(404).send({ error: 'Clan not found.' }); })
54 | .catch((err) => {
55 | logError(err);
56 | res.status(500).send({ error: err.message });
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/scripts/recalculate-elo.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/first */
2 | require('source-map-support').install();
3 |
4 | import Promise from 'bluebird';
5 | import _ from 'lodash';
6 |
7 | import database from '../util/database';
8 | import { calcEloChange } from '../tracker/game';
9 | import { getBaseElo } from '../util/config';
10 |
11 | database('players')
12 | .update({ elo: getBaseElo() })
13 | .then(() => {
14 | database('games')
15 | .where({ gametype: 'duel' })
16 | .orderBy('id', 'asc')
17 | .then(rows => {
18 | const newElos = {};
19 | _.each(rows, row => {
20 | try {
21 | const meta = JSON.parse(row.meta);
22 |
23 | if (!(meta[0] in newElos)) newElos[meta[0]] = getBaseElo();
24 | if (!(meta[2] in newElos)) newElos[meta[2]] = getBaseElo();
25 |
26 | const elod1 = calcEloChange(
27 | newElos[meta[0]],
28 | newElos[meta[2]],
29 | meta[1],
30 | meta[3]
31 | );
32 | const elod2 = calcEloChange(
33 | newElos[meta[2]],
34 | newElos[meta[0]],
35 | meta[3],
36 | meta[1]
37 | );
38 |
39 | newElos[meta[0]] += elod1;
40 | newElos[meta[2]] += elod2;
41 | } catch (e) {
42 | console.log(`Error parsing JSON: ${row.meta}`);
43 | } // eslint-disable-line no-console
44 | });
45 | return newElos;
46 | })
47 | .then(newElos =>
48 | Promise.all(
49 | _.keys(newElos).map(player =>
50 | database('players')
51 | .where({ name: player })
52 | .update({ elo: newElos[player] })
53 | .then()
54 | .catch(() => console.log(`Error updating player Elo: ${player}`))
55 | )
56 | )
57 | ) // eslint-disable-line no-console
58 | .finally(process.exit);
59 | });
60 |
--------------------------------------------------------------------------------
/website/js/main.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import _ from 'lodash';
3 |
4 | const $ = window.jQuery;
5 |
6 | // import '../styles/styles.scss';
7 |
8 | window.dateReflow = function() {
9 | const now = moment();
10 | $('.date').each(function() {
11 | const cd = moment(
12 | `${$(this)
13 | .text()
14 | .replace(/[TZ]/g, ' ')
15 | .trim()}+0000`
16 | );
17 | $(this).attr('title', cd.format('dddd, MMMM Do YYYY, HH:mm:ss'));
18 | if (cd.diff(now, 'hours') >= -22) $(this).text(cd.fromNow());
19 | else if (cd.diff(now, 'days') >= -7)
20 | $(this).text(cd.fromNow() + cd.format(', HH:mm'));
21 | else if (!cd.isSame(now, 'year'))
22 | $(this).text(cd.format('MMMM Do YYYY, HH:mm'));
23 | else if (cd.diff(now, 'days') < -7)
24 | $(this).text(cd.format('MMMM Do, HH:mm'));
25 | $(this).addClass('date-active');
26 | $(this).removeClass('date');
27 | });
28 | };
29 |
30 | window.disableDefault = function() {
31 | $('.disable-default').addClass('disable-default-active');
32 | $('.disable-default-active').click(e => {
33 | e.preventDefault();
34 | });
35 | };
36 |
37 | window.showConnect = function(host, port) {
38 | $('#connect-command').val(`/connect ${host} ${port}`);
39 | $('#connect-info').foundation('open');
40 | $('#connect-command').focus();
41 | };
42 |
43 | window.tryLoadBackground = function(name) {
44 | const bg = new Image();
45 | bg.onload = function() {
46 | $('body').css(
47 | 'background',
48 | `url(/images/mapshots/${name}.jpg) no-repeat center center fixed`
49 | );
50 | $('body').css('background-size', 'cover');
51 | };
52 | bg.src = `/images/mapshots/${name}.jpg`;
53 | };
54 |
55 | $(window.document).ready(() => {
56 | $('.client-side').css('display', 'inline-block');
57 | $(document).foundation();
58 | window.dateReflow();
59 | window.disableDefault();
60 | });
61 |
62 | let pollVisible = false;
63 |
64 | window.togglePoll = function() {
65 | pollVisible = !pollVisible;
66 |
67 | if (pollVisible) {
68 | $('.slidebar').animate({ left: '0px' }, 400, 'linear');
69 | } else {
70 | $('.slidebar').animate({ left: '-490px' }, 400, 'linear');
71 | }
72 | };
73 |
--------------------------------------------------------------------------------
/src/tracker/master.js:
--------------------------------------------------------------------------------
1 | import net from 'net';
2 | import Promise from 'bluebird';
3 | import _ from 'lodash';
4 |
5 | import redis from '../util/redis';
6 | import { logInfo, logError } from '../util/util';
7 | import { getMasterPort, getMasterHost } from '../util/config';
8 |
9 | function pollMasterServer() {
10 | return new Promise((resolve, reject) => {
11 | let agg = '';
12 | const socket = net.connect(getMasterPort(), getMasterHost(), () => {
13 | socket.write('list\n');
14 | });
15 | socket.on('data', data => {
16 | agg += data.toString();
17 | });
18 | socket.on('end', () => {
19 | if (!agg) reject('Masterserver connection failed.');
20 | resolve(agg);
21 | });
22 | socket.on('error', err => {
23 | reject(["Can't poll masterserver:", err]);
24 | });
25 | });
26 | }
27 |
28 | /**
29 | * Poll the master server.
30 | * @returns {Promise} Resolves with an array containing objects of the form
31 | * `{ host: "x.x.x.x", port: 12345 }`.
32 | */
33 | export function getServerList() {
34 | return pollMasterServer().then(result => {
35 | const servers = [];
36 | _.each(result.split('\n'), line => {
37 | const ts = line.split(' ');
38 | if (ts.length === 3 && ts[0] === 'addserver')
39 | servers.push({ host: ts[1], port: parseInt(ts[2], 10) });
40 | });
41 | return servers;
42 | });
43 | }
44 |
45 | /**
46 | * Get the server list from the master server and save it in cache.
47 | */
48 | export function updateServerList() {
49 | return redis.getAsync('last-master-update').then(lastUpdate => {
50 | if (lastUpdate && Date.now() - lastUpdate < 600000) return;
51 | getServerList()
52 | .then(results => {
53 | logInfo(
54 | `Updated server list from master server (${results.length} servers)`
55 | );
56 | return Promise.all([
57 | redis.setAsync('servers', JSON.stringify(results)),
58 | redis.setAsync('last-master-update', Date.now()),
59 | ]);
60 | })
61 | .catch(err => {
62 | logError(err);
63 | return err;
64 | });
65 | });
66 | }
67 |
68 | /**
69 | * Run updateServerList every interval milliseconds.
70 | */
71 | export function start() {
72 | setInterval(updateServerList, 30000);
73 | updateServerList();
74 | }
75 |
--------------------------------------------------------------------------------
/src/api/v2/players.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import app from '../../util/web';
4 | import { round2, escapePostgresLike } from '../../util/util';
5 | import database from '../../util/database';
6 | import playerManager from '../../tracker/player-manager';
7 | import { getCountryName } from '../../util/country';
8 |
9 | export function findPlayers(name, country) {
10 | if (typeof name === 'undefined') name = '';
11 | const query = database('players').where('name', 'ilike', `%${escapePostgresLike(name)}%`);
12 | if (country) {
13 | query.where(function () {
14 | if (country === '__') this.where({ country: '' }).orWhereNull('country');
15 | else this.where({ country });
16 | });
17 | }
18 | return query.orderBy('frags', 'desc').limit(200).then((rows) => {
19 | _.each(rows, (row) => {
20 | row.online = playerManager.isOnline(row.name);
21 | row.country = row.country || '';
22 | row.countryName = getCountryName(row.country);
23 | row.kpd = round2(row.frags / row.deaths);
24 | row.acc = round2(row.accFrags / row.frags);
25 | delete row.accFrags;
26 |
27 | row.instaStats = {
28 | frags: 0, flags: 0, deaths: 0, tks: 0, acc: 0,
29 | };
30 | try {
31 | const stats = JSON.parse(row.instastats);
32 | const [frags, flags, deaths, tks, accFrags] = stats;
33 | row.instaStats = {
34 | frags,
35 | flags,
36 | deaths,
37 | tks,
38 | kpd: round2(frags / deaths) || 0,
39 | acc: round2(accFrags / frags) || 0,
40 | };
41 | } catch (e) {} // eslint-disable-line no-empty
42 | delete row.instastats;
43 |
44 | row.efficStats = {
45 | frags: 0, flags: 0, deaths: 0, tks: 0, acc: 0,
46 | };
47 | try {
48 | const stats = JSON.parse(row.efficstats);
49 | const [frags, flags, deaths, tks, accFrags] = stats;
50 | row.efficStats = {
51 | frags,
52 | flags,
53 | deaths,
54 | tks,
55 | kpd: round2(frags / deaths) || 0,
56 | acc: round2(accFrags / frags) || 0,
57 | };
58 | } catch (e) {} // eslint-disable-line no-empty
59 | delete row.efficstats;
60 | });
61 | return rows;
62 | });
63 | }
64 |
65 | app.get('/api/v2/players/find', (req, res) => {
66 | findPlayers(req.query.name, req.query.country)
67 | .then((results) => { res.send(results); })
68 | .catch((err) => { res.status(500).send({ error: err.message }); });
69 | });
70 |
--------------------------------------------------------------------------------
/src/tracker/player-manager.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import Promise from 'bluebird';
3 |
4 | import { logInfo, logError } from '../util/util';
5 | import database from '../util/database';
6 | import Player from './player';
7 | import { saveSpy } from './spy';
8 |
9 | class PlayerManager {
10 | constructor() {
11 | this.players = {};
12 | this.oldPlayers = {};
13 | this.bans = {};
14 | this.banNames = {};
15 | }
16 |
17 | updatePlayer(gameMode, newState, oldState) {
18 | const { name } = newState;
19 | if (this.banNames[name] || this.bans[newState.ip]) return;
20 | if (!this.players[name]) this.players[name] = new Player(name);
21 | this.players[name].updateState(gameMode, newState, oldState);
22 | }
23 |
24 | flushPlayers() {
25 | const self = this;
26 | return database('players')
27 | .whereIn('name', _.map(self.players, 'name'))
28 | .then(rows => {
29 | rows = _.keyBy(rows, 'name');
30 | const players = _.values(self.players);
31 | const numPlayers = players.length;
32 | return database
33 | .transaction(trx =>
34 | Promise.all(
35 | _.map(players, player => player.saveStats(rows[player.name], trx))
36 | )
37 | )
38 | .then(() => {
39 | self.oldPlayers = self.players;
40 | self.players = {};
41 | return numPlayers;
42 | });
43 | })
44 | .then(numPlayers => {
45 | logInfo(`Players flushed, ${numPlayers} players updated`);
46 | })
47 | .then(saveSpy)
48 | .catch(error => {
49 | logError(`${error}`);
50 | });
51 | }
52 |
53 | isOnline(name) {
54 | return !!(this.players[name] || this.oldPlayers[name]);
55 | }
56 |
57 | start() {
58 | const self = this;
59 | database
60 | .select()
61 | .table('bans')
62 | .then(bans => {
63 | _.each(bans, ban => {
64 | if (ban.ip) self.bans[ban.ip] = true;
65 | if (ban.name) self.banNames[ban.name] = true;
66 | });
67 | });
68 |
69 | setInterval(() => {
70 | this.flushPlayers();
71 | }, 60000);
72 |
73 | process.on('SIGINT', () => {
74 | self.flushPlayers().finally(process.exit);
75 | });
76 | }
77 | }
78 |
79 | const playerManager = new PlayerManager();
80 | export default playerManager;
81 |
--------------------------------------------------------------------------------