From f1467c5a04e9846927a629ac603d5e9cda1ebc65 Mon Sep 17 00:00:00 2001 From: Michael Morrison Date: Wed, 29 Oct 2014 02:02:03 -0500 Subject: [PATCH] crlf -> ln conversion --- README.md | 792 +++++++------- bin/gamedig.js | 68 +- bin/genreadme.js | 38 +- games.txt | 546 +++++----- lib/Class.js | 148 +-- lib/index.js | 160 +-- lib/reader.js | 248 ++--- lib/typeresolver.js | 182 ++-- package.json | 82 +- protocols/americasarmy.js | 40 +- protocols/armagetron.js | 128 +-- protocols/ase.js | 94 +- protocols/battlefield.js | 326 +++--- protocols/buildandshoot.js | 110 +- protocols/core.js | 624 +++++------ protocols/doom3.js | 190 ++-- protocols/ffow.js | 66 +- protocols/gamespy1.js | 170 +-- protocols/gamespy2.js | 192 ++-- protocols/gamespy3.js | 336 +++--- protocols/hexenworld.js | 12 +- protocols/jc2mp.js | 48 +- protocols/killingfloor.js | 12 +- protocols/m2mp.js | 74 +- protocols/minecraftping.js | 186 ++-- protocols/mumble.js | 88 +- protocols/mumbleping.js | 58 +- protocols/mutantfactions.js | 108 +- protocols/nadeo.js | 150 +-- protocols/quake1.js | 14 +- protocols/quake2.js | 170 +-- protocols/quake3.js | 38 +- protocols/samp.js | 180 ++-- protocols/teamspeak2.js | 154 +-- protocols/teamspeak3.js | 148 +-- protocols/terraria.js | 70 +- protocols/unreal2.js | 284 ++--- protocols/ut2004.js | 14 +- protocols/ut3.js | 86 +- protocols/valve.js | 650 +++++------ protocols/ventrilo.js | 480 ++++----- protocols/warsow.js | 24 +- reference/LICENSE | 10 +- reference/bfris/gamedignote.txt | 4 +- reference/bfris/qstat.txt | 552 +++++----- reference/cryengine/gamedignote.txt | 4 +- reference/cryengine/gameq.txt | 200 ++-- reference/cryengine/qstat.txt | 652 +++++------ reference/cs2d/gameq.txt | 112 +- reference/cs2d/lgsl.txt | 140 +-- reference/cube/gameq_cube.txt | 116 +- reference/cube/gameq_sauerbraten.txt | 158 +-- reference/cube/lgsl.txt | 336 +++--- reference/cube/qstat_sauerbraten.txt | 568 +++++----- reference/descent3/gamedignote.txt | 4 +- reference/descent3/qstat.txt | 446 ++++---- reference/farcry/gamedignote.txt | 4 +- reference/farcry/qstat.txt | 522 ++++----- reference/freelancer/gameq.txt | 114 +- reference/freelancer/lgsl.txt | 116 +- reference/ghostrecon/gameq.txt | 128 +-- reference/ghostrecon/lgsl.txt | 266 ++--- reference/ghostrecon/qstat.txt | 1388 ++++++++++++------------ reference/ghostrecon/qstat_info.txt | 456 ++++---- reference/gtr2/lgsl.txt | 80 +- reference/haze/qstat.txt | 1432 ++++++++++++------------- reference/hexen2/gameq.txt | 104 +- reference/openttd/gameq.txt | 152 +-- reference/openttd/lgsl.txt | 128 +-- reference/openttd/qstat.txt | 808 +++++++------- reference/plainsight/lgsl.txt | 66 +- reference/ravenshield/gamedignote.txt | 6 +- reference/ravenshield/qstat.txt | 892 +++++++-------- reference/redfaction/gameq.txt | 118 +- reference/rfactor/gameq.txt | 124 +-- reference/rfactor/lgsl.txt | 188 ++-- reference/samp/lgsl.txt | 184 ++-- reference/savage/gameq.txt | 184 ++-- reference/savage/lgsl.txt | 132 +-- reference/savage/qstat.txt | 556 +++++----- reference/savage2/gameq.txt | 110 +- reference/savage2/lgsl.txt | 92 +- reference/teeworlds/gameq.txt | 130 +-- reference/teeworlds/gameq2.txt | 216 ++-- reference/teeworlds/lgsl.txt | 108 +- reference/teeworlds/qstat.txt | 288 ++--- reference/tribes/gameq.txt | 126 +-- reference/tribes/lgsl.txt | 250 ++--- reference/tribes/qstat.txt | 494 ++++----- reference/tribes2/gameq.txt | 208 ++-- reference/tribes2/lgsl.txt | 144 +-- reference/tribes2/qstat.txt | 910 ++++++++-------- reference/vcmp/lgsl.txt | 180 ++-- reference/worldinconflict/qstat.txt | 482 ++++----- 94 files changed, 11388 insertions(+), 11388 deletions(-) diff --git a/README.md b/README.md index 72bd60e..efe14a1 100644 --- a/README.md +++ b/README.md @@ -1,396 +1,396 @@ -node-GameDig - Game Server Query Library ---- -node-GameDig is a game server query library, capable of querying for the status of -nearly any game or voice server. If a server makes its status publically available, -GameDig can fetch it for you. - -GameDig is available as a node.js module, as well as a -[command line executable](#usage-from-command-line). - -Usage from Node.js ---- - -```shell -npm install gamedig -``` - -```javascript -var Gamedig = require('gamedig'); -Gamedig.query( - { - type: 'minecraft', - host: 'mc.example.com' - }, - function(state) { - if(state.error) console.log("Server is offline"); - else console.log(state); - } -); -``` - -> Is NPM out of date? If you're feeling lucky, you can install the latest code with -> ```shell -> npm install "git+https://github.com/sonicsnes/node-gamedig.git" -> ``` - -### Input Parameters - -* **type**: One of the game IDs listed in the game list below -* **host** -* **port**: (optional) Uses the protocol default if not set -* **notes**: (optional) Passed through to output - -###Callback Function - -The callback function is "guaranteed" to be called exactly once. - -If an error occurs, the returned object will contain an "error" key, indicating the issue. -If the error key exists, it should be assumed that the game server is offline or unreachable. - -Otherwise, the returned object is guaranteed to contain the following keys: - -**Stable, always present:** - -* **name** -* **map** -* **password**: Boolean -* **maxplayers** -* **players**: (array of objects) Each object **may** contain name, ping, score, team, address -* **bots**: Same schema as players -* **notes**: Passed through from the input - -**Unstable, not guaranteed:** - -* **raw**: Contains all information received from the server -* **query**: Details about the query performed - -It can usually be assumed that the number of players online is equal to the length of the players array. -Some servers may return an additional player count number, which may be present in the unstable raw object. - -Games List ---- - -###Supported - - -* Age of Chivalry (ageofchivalry) -* Age of Empires 2 (aoe2) [[Separate Query Port](#separate-query-port)] -* Alien Arena (alienarena) [[Separate Query Port](#separate-query-port)] -* Alien Swarm (alienswarm) -* Aliens vs Predator 2 (avp2) -* Aliens vs Predator 2010 (avp2010) -* America's Army (americasarmy) [[Separate Query Port](#separate-query-port)] -* America's Army 2 (americasarmy2) [[Separate Query Port](#separate-query-port)] -* America's Army 3 (americasarmy3) [[Separate Query Port](#separate-query-port)] -* America's Army: Proving Grounds (americasarmypg) [[Separate Query Port](#separate-query-port)] -* ArmA Armed Assault 1 (arma) -* ArmA Armed Assault 2 (arma2) [[Separate Query Port](#separate-query-port)] -* ArmA Armed Assault 3 (arma3) [[Separate Query Port](#separate-query-port)] -* Armagetron (armagetron) -* Baldur's Gate (baldursgate) [[Separate Query Port](#separate-query-port)] -* Battlefield 1942 (bf1942) [[Separate Query Port](#separate-query-port)] -* Battlefield Vietnam (bfv) [[Separate Query Port](#separate-query-port)] -* Battlefield 2 (bf2) [[Separate Query Port](#separate-query-port)] -* Battlefield 2142 (bf2142) [[Separate Query Port](#separate-query-port)] -* Battlefield: Bad Company 2 (bfbc2) [[Separate Query Port](#separate-query-port)] -* Battlefield 3 (bf3) [[Separate Query Port](#separate-query-port)] -* Battlefield 4 (bf4) [[Separate Query Port](#separate-query-port)] -* Breach (breach) -* Breed (breed) -* Brink (brink) [[Separate Query Port](#separate-query-port)] -* Build and Shoot (buildandshoot) [[Separate Query Port](#separate-query-port)] -* Call of Duty (cod) -* Call of Duty: United Offensive (coduo) -* Call of Duty 2 (cod2) -* Call of Duty 3 (cod3) -* Call of Duty 4: Modern Warfare (cod4) -* Call of Duty: World at War (codwaw) -* Call of Duty: Modern Warfare 2 (codmw2) -* Call of Duty: Modern Warfare 3 (codmw3) [[Separate Query Port](#separate-query-port)] -* Call of Juarez (callofjuarez) [[Separate Query Port](#separate-query-port)] -* Chaser (chaser) [[Separate Query Port](#separate-query-port)] -* Chrome (chrome) [[Separate Query Port](#separate-query-port)] -* Codename Eagle (codenameeagle) [[Separate Query Port](#separate-query-port)] -* Commandos 3: Destination Berlin (commandos3) [[Separate Query Port](#separate-query-port)] -* Command and Conquer: Renegade (cacrenegade) [[Separate Query Port](#separate-query-port)] -* Contact J.A.C.K. (contactjack) [[Separate Query Port](#separate-query-port)] -* Counter-Strike 1.6 (cs16) -* Counter-Strike: Condition Zero (cscz) -* Counter-Strike: Source (css) -* Counter-Strike: Global Offensive (csgo) -* Cross Racing Championship (crossracing) [[Separate Query Port](#separate-query-port)] -* Crysis (crysis) -* Crysis Wars (crysiswars) -* Crysis 2 (crysis2) -* Daikatana (daikatana) [[Separate Query Port](#separate-query-port)] -* Dark Messiah of Might and Magic (dmomam) -* Darkest Hour (darkesthour) [[Separate Query Port](#separate-query-port)] -* DayZ (dayz) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#dayz)] -* Deadly Dozen: Pacific Theater (deadlydozenpt) [[Separate Query Port](#separate-query-port)] -* Deer Hunter 2005 (dh2005) [[Separate Query Port](#separate-query-port)] -* Descent 3 (descent3) [[Separate Query Port](#separate-query-port)] -* Deus Ex (deusex) [[Separate Query Port](#separate-query-port)] -* Devastation (devastation) [[Separate Query Port](#separate-query-port)] -* Dino D-Day (dinodday) -* Dirt Track Racing 2 (dirttrackracing2) [[Separate Query Port](#separate-query-port)] -* Day of Defeat (dod) -* Day of Defeat: Source (dods) -* Doom 3 (doom3) -* DOTA 2 (dota2) -* Drakan (drakan) [[Separate Query Port](#separate-query-port)] -* Enemy Territory Quake Wars (etqw) [[Separate Query Port](#separate-query-port)] -* F.E.A.R. (fear) [[Separate Query Port](#separate-query-port)] -* F1 2002 (f12002) [[Separate Query Port](#separate-query-port)] -* F1 Challenge 99-02 (f1c9902) [[Separate Query Port](#separate-query-port)] -* Far Cry (farcry) [[Separate Query Port](#separate-query-port)] -* Far Cry (farcry2) [[Separate Query Port](#separate-query-port)] -* Fortress Forever (fortressforever) -* Flashpoint (flashpoint) [[Separate Query Port](#separate-query-port)] -* Frontlines: Fuel of War (ffow) [[Separate Query Port](#separate-query-port)] -* Garry's Mod (garrysmod) -* Ghost Recon: Advanced Warfighter (graw) [[Separate Query Port](#separate-query-port)] -* Ghost Recon: Advanced Warfighter 2 (graw2) [[Separate Query Port](#separate-query-port)] -* Giants: Citizen Kabuto (giantscitizenkabuto) [[Separate Query Port](#separate-query-port)] -* Global Operations (globaloperations) [[Separate Query Port](#separate-query-port)] -* Gore (gore) [[Separate Query Port](#separate-query-port)] -* Gunman Chronicles (gunmanchronicles) -* Half-Life 1 Deathmatch (hldm) -* Half-Life 2 Deathmatch (hl2dm) -* Halo (halo) -* Halo 2 (halo2) -* Heretic 2 (heretic2) [[Separate Query Port](#separate-query-port)] -* Hexen World (hexenworld) [[Separate Query Port](#separate-query-port)] -* The Hidden: Source (hidden) -* Hidden and Dangerous 2 (had2) [[Separate Query Port](#separate-query-port)] -* Homefront (homefront) -* Homeworld 2 (homeworld2) [[Separate Query Port](#separate-query-port)] -* IGI-2: Covert Strike (igi2) [[Separate Query Port](#separate-query-port)] -* IL-2 Sturmovik (il2) [[Separate Query Port](#separate-query-port)] -* Insurgency (insurgency) -* Iron Storm (ironstorm) [[Separate Query Port](#separate-query-port)] -* James Bond: Nightfire (jamesbondnightfire) [[Separate Query Port](#separate-query-port)] -* Just Cause 2 Multiplayer (jc2mp) -* Killing Floor (killingfloor) [[Separate Query Port](#separate-query-port)] -* Kingpin: Life of Crime (kingpin) [[Separate Query Port](#separate-query-port)] -* KISS Psycho Circus (kisspc) [[Separate Query Port](#separate-query-port)] -* KzMod (kzmod) -* Left 4 Dead (left4dead) -* Left 4 Dead 2 (left4dead2) -* Mafia 2 Multiplayer (m2mp) [[Separate Query Port](#separate-query-port)] -* Medal of Honor: Allied Assault (mohaa) [[Separate Query Port](#separate-query-port)] -* Medal of Honor: Pacific Assault (mohpa) [[Separate Query Port](#separate-query-port)] -* Medal of Honor: Airborne (mohab) [[Separate Query Port](#separate-query-port)] -* Medal of Honor: Spearhead (mohsh) [[Separate Query Port](#separate-query-port)] -* Medal of Honor: Breakthrough (mohbt) [[Separate Query Port](#separate-query-port)] -* Medal of Honor 2010 (moh2010) [[Separate Query Port](#separate-query-port)] -* Medal of Honor: Warfighter (mohwf) [[Separate Query Port](#separate-query-port)] -* Minecraft (minecraft) [[Additional Notes](#minecraft)] -* Minecraft (minecraftping) [[Additional Notes](#minecraft)] -* Monday Night Combat (mnc) [[Separate Query Port](#separate-query-port)] -* Multi Theft Auto: Vice City (mtavc) [[Separate Query Port](#separate-query-port)] -* Multi Theft Auto: San Andreas (mtasa) [[Separate Query Port](#separate-query-port)] -* Mumble (mumble) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#mumble)] -* Mumble (mumbleping) [[Additional Notes](#mumble)] -* Mutant Factions (mutantfactions) -* Nascar Thunder 2004 (nascarthunder2004) [[Separate Query Port](#separate-query-port)] -* netPanzer (netpanzer) -* No More Room in Hell (nmrih) -* Natural Selection (ns) -* Natural Selection 2 (ns2) [[Separate Query Port](#separate-query-port)] -* Need for Speed: Hot Pursuit 2 (nfshp2) [[Separate Query Port](#separate-query-port)] -* Nerf Arena Blast (nab) [[Separate Query Port](#separate-query-port)] -* Neverwinter Nights (nwn) [[Separate Query Port](#separate-query-port)] -* Neverwinter Nights 2 (nwn2) [[Separate Query Port](#separate-query-port)] -* Nexuiz (nexuiz) [[Separate Query Port](#separate-query-port)] -* Nitro Family (nitrofamily) [[Separate Query Port](#separate-query-port)] -* No One Lives Forever (nolf) [[Separate Query Port](#separate-query-port)] -* No One Lives Forever 2 (nolf2) [[Separate Query Port](#separate-query-port)] -* Nuclear Dawn (nucleardawn) -* OpenArena (openarena) [[Separate Query Port](#separate-query-port)] -* Operation Flashpoint (operationflashpoint) [[Separate Query Port](#separate-query-port)] -* Painkiller (painkiller) [[Separate Query Port](#separate-query-port)] -* Postal 2 (postal2) [[Separate Query Port](#separate-query-port)] -* Prey (prey) [[Separate Query Port](#separate-query-port)] -* Quake 1: QuakeWorld (quake1) -* Quake 2 (quake2) -* Quake 3: Arena (quake3) -* Quake 4 (quake4) -* Rag Doll Kung Fu (ragdollkungfu) -* Rainbow Six (r6) [[Separate Query Port](#separate-query-port)] -* Rainbow Six 2: Rogue Spear (r6roguespear) [[Separate Query Port](#separate-query-port)] -* Rainbow Six 3: Raven Shield (r6ravenshield) [[Separate Query Port](#separate-query-port)] -* RalliSport Challenge (rallisportchallenge) [[Separate Query Port](#separate-query-port)] -* Rally Masters (rallymasters) [[Separate Query Port](#separate-query-port)] -* Red Orchestra (redorchestra) [[Separate Query Port](#separate-query-port)] -* Red Orchestra: Ostfront 41-45 (redorchestraost) [[Separate Query Port](#separate-query-port)] -* Red Orchestra 2 (redorchestra2) [[Separate Query Port](#separate-query-port)] -* Redline (redline) [[Separate Query Port](#separate-query-port)] -* Return to Castle Wolfenstein (rtcw) [[Separate Query Port](#separate-query-port)] -* Ricochet (ricochet) -* Rise of Nations (riseofnations) [[Separate Query Port](#separate-query-port)] -* Rune (rune) [[Separate Query Port](#separate-query-port)] -* Rust (rust) [[Separate Query Port](#separate-query-port)] -* San Andreas Multiplayer (samp) -* Serious Sam (ss) [[Separate Query Port](#separate-query-port)] -* Serious Sam 2 (ss2) -* Shattered Horizon (shatteredhorizon) -* The Ship (ship) -* Shogo (shogo) [[Separate Query Port](#separate-query-port)] -* Shootmania (shootmania) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] -* SiN (sin) [[Separate Query Port](#separate-query-port)] -* SiN Episodes (sinep) -* Soldat (soldat) [[Separate Query Port](#separate-query-port)] -* Soldier of Fortune (sof) [[Separate Query Port](#separate-query-port)] -* Soldier of Fortune 2 (sof2) [[Separate Query Port](#separate-query-port)] -* S.T.A.L.K.E.R. (stalker) [[Separate Query Port](#separate-query-port)] -* Star Trek: Bridge Commander (stbc) [[Separate Query Port](#separate-query-port)] -* Star Trek: Voyager - Elite Force (stvef) [[Separate Query Port](#separate-query-port)] -* Star Trek: Voyager - Elite Force 2 (stvef2) [[Separate Query Port](#separate-query-port)] -* Star Wars: Battlefront (swbf) [[Separate Query Port](#separate-query-port)] -* Star Wars: Battlefront 2 (swbf2) [[Separate Query Port](#separate-query-port)] -* Star Wars: Jedi Knight (swjk) [[Separate Query Port](#separate-query-port)] -* Star Wars: Jedi Knight 2 (swjk2) [[Separate Query Port](#separate-query-port)] -* Star Wars: Republic Commando (swrc) [[Separate Query Port](#separate-query-port)] -* Starbound (starbound) -* Suicide Survival (suicidesurvival) -* SWAT 4 (swat4) [[Separate Query Port](#separate-query-port)] -* Sven Coop (svencoop) -* Synergy (synergy) -* Tactical Ops (tacticalops) [[Separate Query Port](#separate-query-port)] -* Team Factor (teamfactor) [[Separate Query Port](#separate-query-port)] -* Team Fortress Classic (tfc) -* Team Fortress 2 (tf2) -* Teamspeak 2 (teamspeak2) [[Separate Query Port](#separate-query-port)] -* Teamspeak 3 (teamspeak3) [[Separate Query Port](#separate-query-port)] -* Terminus (terminus) [[Separate Query Port](#separate-query-port)] -* Terraria (terraria) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#terraria)] -* Tony Hawk's Pro Skater 3 (thps3) [[Separate Query Port](#separate-query-port)] -* Tony Hawk's Pro Skater 4 (thps4) [[Separate Query Port](#separate-query-port)] -* Tony Hawk's Underground 2 (thu2) [[Separate Query Port](#separate-query-port)] -* Trackmania 2 (trackmania2) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] -* Trackmania Forever (trackmaniaforever) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] -* Tremulous (tremulous) [[Separate Query Port](#separate-query-port)] -* Tribes: Vengeance (tribesvengeance) [[Separate Query Port](#separate-query-port)] -* Tron 2.0 (tron20) [[Separate Query Port](#separate-query-port)] -* Turok 2 (turok2) [[Separate Query Port](#separate-query-port)] -* Universal Combat (universalcombat) [[Separate Query Port](#separate-query-port)] -* Unreal (unreal) [[Separate Query Port](#separate-query-port)] -* Unreal Tournament (ut) [[Separate Query Port](#separate-query-port)] -* Unreal Tournament 2003 (ut2003) [[Separate Query Port](#separate-query-port)] -* Unreal Tournament 2004 (ut2004) [[Separate Query Port](#separate-query-port)] -* Unreal Tournament 3 (ut3) [[Separate Query Port](#separate-query-port)] -* Urban Terror (urbanterror) [[Separate Query Port](#separate-query-port)] -* V8 Supercar Challenge (v8supercar) [[Separate Query Port](#separate-query-port)] -* Ventrilo (ventrilo) -* Vietcong (vietcong) [[Separate Query Port](#separate-query-port)] -* Vietcong 2 (vietcong2) [[Separate Query Port](#separate-query-port)] -* Warsow (warsow) -* Wheel of Time (wheeloftime) [[Separate Query Port](#separate-query-port)] -* Wolfenstein 2009 (wolfenstein2009) [[Separate Query Port](#separate-query-port)] -* Wolfenstein: Enemy Territory (wolfensteinet) [[Separate Query Port](#separate-query-port)] -* Xpand Rally (xpandrally) [[Separate Query Port](#separate-query-port)] -* Zombie Master (zombiemaster) -* Zombie Panic: Source (zps) - - - -###Not supported (yet) - -* rFactor Engine (rfactor): - * rFactor - * Arca Sim Racing -* Cube Engine (cube): - * Cube 1 - * Assault Cube - * Cube 2: Sauerbraten - * Blood Frontier -* BFRIS -* Call of Duty: Black Ops 1 and 2 (no documentation, may require rcon) -* Counter-Strike 2D -* Freelancer -* Ghost Recon -* GTR2 -* Haze -* Hexen 2 -* OpenTTD -* Plain Sight -* Red Faction -* Savage: Battle for Newerth -* Savage 2: A Tortured Soul -* Sum of All Fears -* Teeworlds -* Tribes 1: Starsiege -* Tribes 2 -* Vice City Multiplayer -* World in Conflict - -> Want support for one of these games? Please open an issue to show your interest! -> __Know how to code?__ Protocols for most of the games above are documented -> in the /reference folder, ready for you to develop into GameDig! - - - -> Don't see your game listed here? -> -> First, let us know so we can fix it. Then, you can try using some common query -> protocols directly by using one of these server types: -> * protocol-ase -> * protocol-battlefield -> * protocol-doom3 -> * protocol-gamespy1 -> * protocol-gamespy2 -> * protocol-gamespy3 -> * protocol-nadeo -> * protocol-quake2 -> * protocol-quake3 -> * protocol-unreal2 -> * protocol-valve - -Games with Additional Notes ---- - -### DayZ -DayZ uses a query port that is separate from its main game port. The query port is usually -the game port PLUS 24714 or 24715. You may need to pass this port in as the 'port_query' request option. - -### Minecraft -Some minecraft servers may not respond to a typical status query. If this is the case, try using the -'minecraftping' server type instead, which uses a less accurate but more reliable solution. - -### Mumble -For full query results from Mumble, you must be running the -[GTmurmur plugin](http://www.gametracker.com/downloads/gtmurmurplugin.php). -If you do not wish to run the plugin, or do not require details such as channel and user lists, -you can use the 'mumbleping' server type instead, which uses a less accurate but more reliable solution - -### Nadeo (ShootMania / TrackMania / etc) -The server must have xmlrpc enabled, and you must pass the xmlrpc port to GameDig, not the connection port. -You must have a user account on the server with access level User or higher. -Pass the login into to GameDig with the additional options: login, password - -### Terraria -Requires tshock server mod, and a REST user token, which can be passed to GameDig with the -additional option: token - -### Separate Query Port -Games with this note use a query port which is usually not the same as the game's connection port. -Usually, no action will be required from you. The 'port' option you pass GameDig should be the game's -connection port. GameDig will attempt to calculate the query port automatically. If the query still fails, -you may need to pass the 'port_query' option to GameDig as well, indicating the separate query port. - -Usage from Command Line ---- - -Want to integrate server queries from a batch script or other programming language? -You'll still need npm to install gamedig: -```shell -npm install gamedig -g -``` - -After installing gamedig globally, you can call gamedig via the command line -using the same parameters mentioned in the API above: -```shell -gamedig --type minecraft --host mc.example.com --port 11234 -``` - -The output of the command will be in JSON format. +node-GameDig - Game Server Query Library +--- +node-GameDig is a game server query library, capable of querying for the status of +nearly any game or voice server. If a server makes its status publically available, +GameDig can fetch it for you. + +GameDig is available as a node.js module, as well as a +[command line executable](#usage-from-command-line). + +Usage from Node.js +--- + +```shell +npm install gamedig +``` + +```javascript +var Gamedig = require('gamedig'); +Gamedig.query( + { + type: 'minecraft', + host: 'mc.example.com' + }, + function(state) { + if(state.error) console.log("Server is offline"); + else console.log(state); + } +); +``` + +> Is NPM out of date? If you're feeling lucky, you can install the latest code with +> ```shell +> npm install "git+https://github.com/sonicsnes/node-gamedig.git" +> ``` + +### Input Parameters + +* **type**: One of the game IDs listed in the game list below +* **host** +* **port**: (optional) Uses the protocol default if not set +* **notes**: (optional) Passed through to output + +###Callback Function + +The callback function is "guaranteed" to be called exactly once. + +If an error occurs, the returned object will contain an "error" key, indicating the issue. +If the error key exists, it should be assumed that the game server is offline or unreachable. + +Otherwise, the returned object is guaranteed to contain the following keys: + +**Stable, always present:** + +* **name** +* **map** +* **password**: Boolean +* **maxplayers** +* **players**: (array of objects) Each object **may** contain name, ping, score, team, address +* **bots**: Same schema as players +* **notes**: Passed through from the input + +**Unstable, not guaranteed:** + +* **raw**: Contains all information received from the server +* **query**: Details about the query performed + +It can usually be assumed that the number of players online is equal to the length of the players array. +Some servers may return an additional player count number, which may be present in the unstable raw object. + +Games List +--- + +###Supported + + +* Age of Chivalry (ageofchivalry) +* Age of Empires 2 (aoe2) [[Separate Query Port](#separate-query-port)] +* Alien Arena (alienarena) [[Separate Query Port](#separate-query-port)] +* Alien Swarm (alienswarm) +* Aliens vs Predator 2 (avp2) +* Aliens vs Predator 2010 (avp2010) +* America's Army (americasarmy) [[Separate Query Port](#separate-query-port)] +* America's Army 2 (americasarmy2) [[Separate Query Port](#separate-query-port)] +* America's Army 3 (americasarmy3) [[Separate Query Port](#separate-query-port)] +* America's Army: Proving Grounds (americasarmypg) [[Separate Query Port](#separate-query-port)] +* ArmA Armed Assault 1 (arma) +* ArmA Armed Assault 2 (arma2) [[Separate Query Port](#separate-query-port)] +* ArmA Armed Assault 3 (arma3) [[Separate Query Port](#separate-query-port)] +* Armagetron (armagetron) +* Baldur's Gate (baldursgate) [[Separate Query Port](#separate-query-port)] +* Battlefield 1942 (bf1942) [[Separate Query Port](#separate-query-port)] +* Battlefield Vietnam (bfv) [[Separate Query Port](#separate-query-port)] +* Battlefield 2 (bf2) [[Separate Query Port](#separate-query-port)] +* Battlefield 2142 (bf2142) [[Separate Query Port](#separate-query-port)] +* Battlefield: Bad Company 2 (bfbc2) [[Separate Query Port](#separate-query-port)] +* Battlefield 3 (bf3) [[Separate Query Port](#separate-query-port)] +* Battlefield 4 (bf4) [[Separate Query Port](#separate-query-port)] +* Breach (breach) +* Breed (breed) +* Brink (brink) [[Separate Query Port](#separate-query-port)] +* Build and Shoot (buildandshoot) [[Separate Query Port](#separate-query-port)] +* Call of Duty (cod) +* Call of Duty: United Offensive (coduo) +* Call of Duty 2 (cod2) +* Call of Duty 3 (cod3) +* Call of Duty 4: Modern Warfare (cod4) +* Call of Duty: World at War (codwaw) +* Call of Duty: Modern Warfare 2 (codmw2) +* Call of Duty: Modern Warfare 3 (codmw3) [[Separate Query Port](#separate-query-port)] +* Call of Juarez (callofjuarez) [[Separate Query Port](#separate-query-port)] +* Chaser (chaser) [[Separate Query Port](#separate-query-port)] +* Chrome (chrome) [[Separate Query Port](#separate-query-port)] +* Codename Eagle (codenameeagle) [[Separate Query Port](#separate-query-port)] +* Commandos 3: Destination Berlin (commandos3) [[Separate Query Port](#separate-query-port)] +* Command and Conquer: Renegade (cacrenegade) [[Separate Query Port](#separate-query-port)] +* Contact J.A.C.K. (contactjack) [[Separate Query Port](#separate-query-port)] +* Counter-Strike 1.6 (cs16) +* Counter-Strike: Condition Zero (cscz) +* Counter-Strike: Source (css) +* Counter-Strike: Global Offensive (csgo) +* Cross Racing Championship (crossracing) [[Separate Query Port](#separate-query-port)] +* Crysis (crysis) +* Crysis Wars (crysiswars) +* Crysis 2 (crysis2) +* Daikatana (daikatana) [[Separate Query Port](#separate-query-port)] +* Dark Messiah of Might and Magic (dmomam) +* Darkest Hour (darkesthour) [[Separate Query Port](#separate-query-port)] +* DayZ (dayz) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#dayz)] +* Deadly Dozen: Pacific Theater (deadlydozenpt) [[Separate Query Port](#separate-query-port)] +* Deer Hunter 2005 (dh2005) [[Separate Query Port](#separate-query-port)] +* Descent 3 (descent3) [[Separate Query Port](#separate-query-port)] +* Deus Ex (deusex) [[Separate Query Port](#separate-query-port)] +* Devastation (devastation) [[Separate Query Port](#separate-query-port)] +* Dino D-Day (dinodday) +* Dirt Track Racing 2 (dirttrackracing2) [[Separate Query Port](#separate-query-port)] +* Day of Defeat (dod) +* Day of Defeat: Source (dods) +* Doom 3 (doom3) +* DOTA 2 (dota2) +* Drakan (drakan) [[Separate Query Port](#separate-query-port)] +* Enemy Territory Quake Wars (etqw) [[Separate Query Port](#separate-query-port)] +* F.E.A.R. (fear) [[Separate Query Port](#separate-query-port)] +* F1 2002 (f12002) [[Separate Query Port](#separate-query-port)] +* F1 Challenge 99-02 (f1c9902) [[Separate Query Port](#separate-query-port)] +* Far Cry (farcry) [[Separate Query Port](#separate-query-port)] +* Far Cry (farcry2) [[Separate Query Port](#separate-query-port)] +* Fortress Forever (fortressforever) +* Flashpoint (flashpoint) [[Separate Query Port](#separate-query-port)] +* Frontlines: Fuel of War (ffow) [[Separate Query Port](#separate-query-port)] +* Garry's Mod (garrysmod) +* Ghost Recon: Advanced Warfighter (graw) [[Separate Query Port](#separate-query-port)] +* Ghost Recon: Advanced Warfighter 2 (graw2) [[Separate Query Port](#separate-query-port)] +* Giants: Citizen Kabuto (giantscitizenkabuto) [[Separate Query Port](#separate-query-port)] +* Global Operations (globaloperations) [[Separate Query Port](#separate-query-port)] +* Gore (gore) [[Separate Query Port](#separate-query-port)] +* Gunman Chronicles (gunmanchronicles) +* Half-Life 1 Deathmatch (hldm) +* Half-Life 2 Deathmatch (hl2dm) +* Halo (halo) +* Halo 2 (halo2) +* Heretic 2 (heretic2) [[Separate Query Port](#separate-query-port)] +* Hexen World (hexenworld) [[Separate Query Port](#separate-query-port)] +* The Hidden: Source (hidden) +* Hidden and Dangerous 2 (had2) [[Separate Query Port](#separate-query-port)] +* Homefront (homefront) +* Homeworld 2 (homeworld2) [[Separate Query Port](#separate-query-port)] +* IGI-2: Covert Strike (igi2) [[Separate Query Port](#separate-query-port)] +* IL-2 Sturmovik (il2) [[Separate Query Port](#separate-query-port)] +* Insurgency (insurgency) +* Iron Storm (ironstorm) [[Separate Query Port](#separate-query-port)] +* James Bond: Nightfire (jamesbondnightfire) [[Separate Query Port](#separate-query-port)] +* Just Cause 2 Multiplayer (jc2mp) +* Killing Floor (killingfloor) [[Separate Query Port](#separate-query-port)] +* Kingpin: Life of Crime (kingpin) [[Separate Query Port](#separate-query-port)] +* KISS Psycho Circus (kisspc) [[Separate Query Port](#separate-query-port)] +* KzMod (kzmod) +* Left 4 Dead (left4dead) +* Left 4 Dead 2 (left4dead2) +* Mafia 2 Multiplayer (m2mp) [[Separate Query Port](#separate-query-port)] +* Medal of Honor: Allied Assault (mohaa) [[Separate Query Port](#separate-query-port)] +* Medal of Honor: Pacific Assault (mohpa) [[Separate Query Port](#separate-query-port)] +* Medal of Honor: Airborne (mohab) [[Separate Query Port](#separate-query-port)] +* Medal of Honor: Spearhead (mohsh) [[Separate Query Port](#separate-query-port)] +* Medal of Honor: Breakthrough (mohbt) [[Separate Query Port](#separate-query-port)] +* Medal of Honor 2010 (moh2010) [[Separate Query Port](#separate-query-port)] +* Medal of Honor: Warfighter (mohwf) [[Separate Query Port](#separate-query-port)] +* Minecraft (minecraft) [[Additional Notes](#minecraft)] +* Minecraft (minecraftping) [[Additional Notes](#minecraft)] +* Monday Night Combat (mnc) [[Separate Query Port](#separate-query-port)] +* Multi Theft Auto: Vice City (mtavc) [[Separate Query Port](#separate-query-port)] +* Multi Theft Auto: San Andreas (mtasa) [[Separate Query Port](#separate-query-port)] +* Mumble (mumble) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#mumble)] +* Mumble (mumbleping) [[Additional Notes](#mumble)] +* Mutant Factions (mutantfactions) +* Nascar Thunder 2004 (nascarthunder2004) [[Separate Query Port](#separate-query-port)] +* netPanzer (netpanzer) +* No More Room in Hell (nmrih) +* Natural Selection (ns) +* Natural Selection 2 (ns2) [[Separate Query Port](#separate-query-port)] +* Need for Speed: Hot Pursuit 2 (nfshp2) [[Separate Query Port](#separate-query-port)] +* Nerf Arena Blast (nab) [[Separate Query Port](#separate-query-port)] +* Neverwinter Nights (nwn) [[Separate Query Port](#separate-query-port)] +* Neverwinter Nights 2 (nwn2) [[Separate Query Port](#separate-query-port)] +* Nexuiz (nexuiz) [[Separate Query Port](#separate-query-port)] +* Nitro Family (nitrofamily) [[Separate Query Port](#separate-query-port)] +* No One Lives Forever (nolf) [[Separate Query Port](#separate-query-port)] +* No One Lives Forever 2 (nolf2) [[Separate Query Port](#separate-query-port)] +* Nuclear Dawn (nucleardawn) +* OpenArena (openarena) [[Separate Query Port](#separate-query-port)] +* Operation Flashpoint (operationflashpoint) [[Separate Query Port](#separate-query-port)] +* Painkiller (painkiller) [[Separate Query Port](#separate-query-port)] +* Postal 2 (postal2) [[Separate Query Port](#separate-query-port)] +* Prey (prey) [[Separate Query Port](#separate-query-port)] +* Quake 1: QuakeWorld (quake1) +* Quake 2 (quake2) +* Quake 3: Arena (quake3) +* Quake 4 (quake4) +* Rag Doll Kung Fu (ragdollkungfu) +* Rainbow Six (r6) [[Separate Query Port](#separate-query-port)] +* Rainbow Six 2: Rogue Spear (r6roguespear) [[Separate Query Port](#separate-query-port)] +* Rainbow Six 3: Raven Shield (r6ravenshield) [[Separate Query Port](#separate-query-port)] +* RalliSport Challenge (rallisportchallenge) [[Separate Query Port](#separate-query-port)] +* Rally Masters (rallymasters) [[Separate Query Port](#separate-query-port)] +* Red Orchestra (redorchestra) [[Separate Query Port](#separate-query-port)] +* Red Orchestra: Ostfront 41-45 (redorchestraost) [[Separate Query Port](#separate-query-port)] +* Red Orchestra 2 (redorchestra2) [[Separate Query Port](#separate-query-port)] +* Redline (redline) [[Separate Query Port](#separate-query-port)] +* Return to Castle Wolfenstein (rtcw) [[Separate Query Port](#separate-query-port)] +* Ricochet (ricochet) +* Rise of Nations (riseofnations) [[Separate Query Port](#separate-query-port)] +* Rune (rune) [[Separate Query Port](#separate-query-port)] +* Rust (rust) [[Separate Query Port](#separate-query-port)] +* San Andreas Multiplayer (samp) +* Serious Sam (ss) [[Separate Query Port](#separate-query-port)] +* Serious Sam 2 (ss2) +* Shattered Horizon (shatteredhorizon) +* The Ship (ship) +* Shogo (shogo) [[Separate Query Port](#separate-query-port)] +* Shootmania (shootmania) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] +* SiN (sin) [[Separate Query Port](#separate-query-port)] +* SiN Episodes (sinep) +* Soldat (soldat) [[Separate Query Port](#separate-query-port)] +* Soldier of Fortune (sof) [[Separate Query Port](#separate-query-port)] +* Soldier of Fortune 2 (sof2) [[Separate Query Port](#separate-query-port)] +* S.T.A.L.K.E.R. (stalker) [[Separate Query Port](#separate-query-port)] +* Star Trek: Bridge Commander (stbc) [[Separate Query Port](#separate-query-port)] +* Star Trek: Voyager - Elite Force (stvef) [[Separate Query Port](#separate-query-port)] +* Star Trek: Voyager - Elite Force 2 (stvef2) [[Separate Query Port](#separate-query-port)] +* Star Wars: Battlefront (swbf) [[Separate Query Port](#separate-query-port)] +* Star Wars: Battlefront 2 (swbf2) [[Separate Query Port](#separate-query-port)] +* Star Wars: Jedi Knight (swjk) [[Separate Query Port](#separate-query-port)] +* Star Wars: Jedi Knight 2 (swjk2) [[Separate Query Port](#separate-query-port)] +* Star Wars: Republic Commando (swrc) [[Separate Query Port](#separate-query-port)] +* Starbound (starbound) +* Suicide Survival (suicidesurvival) +* SWAT 4 (swat4) [[Separate Query Port](#separate-query-port)] +* Sven Coop (svencoop) +* Synergy (synergy) +* Tactical Ops (tacticalops) [[Separate Query Port](#separate-query-port)] +* Team Factor (teamfactor) [[Separate Query Port](#separate-query-port)] +* Team Fortress Classic (tfc) +* Team Fortress 2 (tf2) +* Teamspeak 2 (teamspeak2) [[Separate Query Port](#separate-query-port)] +* Teamspeak 3 (teamspeak3) [[Separate Query Port](#separate-query-port)] +* Terminus (terminus) [[Separate Query Port](#separate-query-port)] +* Terraria (terraria) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#terraria)] +* Tony Hawk's Pro Skater 3 (thps3) [[Separate Query Port](#separate-query-port)] +* Tony Hawk's Pro Skater 4 (thps4) [[Separate Query Port](#separate-query-port)] +* Tony Hawk's Underground 2 (thu2) [[Separate Query Port](#separate-query-port)] +* Trackmania 2 (trackmania2) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] +* Trackmania Forever (trackmaniaforever) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] +* Tremulous (tremulous) [[Separate Query Port](#separate-query-port)] +* Tribes: Vengeance (tribesvengeance) [[Separate Query Port](#separate-query-port)] +* Tron 2.0 (tron20) [[Separate Query Port](#separate-query-port)] +* Turok 2 (turok2) [[Separate Query Port](#separate-query-port)] +* Universal Combat (universalcombat) [[Separate Query Port](#separate-query-port)] +* Unreal (unreal) [[Separate Query Port](#separate-query-port)] +* Unreal Tournament (ut) [[Separate Query Port](#separate-query-port)] +* Unreal Tournament 2003 (ut2003) [[Separate Query Port](#separate-query-port)] +* Unreal Tournament 2004 (ut2004) [[Separate Query Port](#separate-query-port)] +* Unreal Tournament 3 (ut3) [[Separate Query Port](#separate-query-port)] +* Urban Terror (urbanterror) [[Separate Query Port](#separate-query-port)] +* V8 Supercar Challenge (v8supercar) [[Separate Query Port](#separate-query-port)] +* Ventrilo (ventrilo) +* Vietcong (vietcong) [[Separate Query Port](#separate-query-port)] +* Vietcong 2 (vietcong2) [[Separate Query Port](#separate-query-port)] +* Warsow (warsow) +* Wheel of Time (wheeloftime) [[Separate Query Port](#separate-query-port)] +* Wolfenstein 2009 (wolfenstein2009) [[Separate Query Port](#separate-query-port)] +* Wolfenstein: Enemy Territory (wolfensteinet) [[Separate Query Port](#separate-query-port)] +* Xpand Rally (xpandrally) [[Separate Query Port](#separate-query-port)] +* Zombie Master (zombiemaster) +* Zombie Panic: Source (zps) + + + +###Not supported (yet) + +* rFactor Engine (rfactor): + * rFactor + * Arca Sim Racing +* Cube Engine (cube): + * Cube 1 + * Assault Cube + * Cube 2: Sauerbraten + * Blood Frontier +* BFRIS +* Call of Duty: Black Ops 1 and 2 (no documentation, may require rcon) +* Counter-Strike 2D +* Freelancer +* Ghost Recon +* GTR2 +* Haze +* Hexen 2 +* OpenTTD +* Plain Sight +* Red Faction +* Savage: Battle for Newerth +* Savage 2: A Tortured Soul +* Sum of All Fears +* Teeworlds +* Tribes 1: Starsiege +* Tribes 2 +* Vice City Multiplayer +* World in Conflict + +> Want support for one of these games? Please open an issue to show your interest! +> __Know how to code?__ Protocols for most of the games above are documented +> in the /reference folder, ready for you to develop into GameDig! + + + +> Don't see your game listed here? +> +> First, let us know so we can fix it. Then, you can try using some common query +> protocols directly by using one of these server types: +> * protocol-ase +> * protocol-battlefield +> * protocol-doom3 +> * protocol-gamespy1 +> * protocol-gamespy2 +> * protocol-gamespy3 +> * protocol-nadeo +> * protocol-quake2 +> * protocol-quake3 +> * protocol-unreal2 +> * protocol-valve + +Games with Additional Notes +--- + +### DayZ +DayZ uses a query port that is separate from its main game port. The query port is usually +the game port PLUS 24714 or 24715. You may need to pass this port in as the 'port_query' request option. + +### Minecraft +Some minecraft servers may not respond to a typical status query. If this is the case, try using the +'minecraftping' server type instead, which uses a less accurate but more reliable solution. + +### Mumble +For full query results from Mumble, you must be running the +[GTmurmur plugin](http://www.gametracker.com/downloads/gtmurmurplugin.php). +If you do not wish to run the plugin, or do not require details such as channel and user lists, +you can use the 'mumbleping' server type instead, which uses a less accurate but more reliable solution + +### Nadeo (ShootMania / TrackMania / etc) +The server must have xmlrpc enabled, and you must pass the xmlrpc port to GameDig, not the connection port. +You must have a user account on the server with access level User or higher. +Pass the login into to GameDig with the additional options: login, password + +### Terraria +Requires tshock server mod, and a REST user token, which can be passed to GameDig with the +additional option: token + +### Separate Query Port +Games with this note use a query port which is usually not the same as the game's connection port. +Usually, no action will be required from you. The 'port' option you pass GameDig should be the game's +connection port. GameDig will attempt to calculate the query port automatically. If the query still fails, +you may need to pass the 'port_query' option to GameDig as well, indicating the separate query port. + +Usage from Command Line +--- + +Want to integrate server queries from a batch script or other programming language? +You'll still need npm to install gamedig: +```shell +npm install gamedig -g +``` + +After installing gamedig globally, you can call gamedig via the command line +using the same parameters mentioned in the API above: +```shell +gamedig --type minecraft --host mc.example.com --port 11234 +``` + +The output of the command will be in JSON format. diff --git a/bin/gamedig.js b/bin/gamedig.js index 87c3ff6..37d9a1d 100644 --- a/bin/gamedig.js +++ b/bin/gamedig.js @@ -1,34 +1,34 @@ -#!/usr/bin/env node - -var argv = require('optimist').argv; - -var debug = argv.debug; -delete argv.debug; -var outputFormat = argv.output; -delete argv.output; - -var options = {}; -for(var key in argv) { - var value = argv[key]; - if( - key == '_' - || key.charAt(0) == '$' - || (typeof value != 'string' && typeof value != 'number') - ) - continue; - options[key] = value; -} - -var Gamedig = require('../lib/index'); -if(debug) Gamedig.debug = true; -Gamedig.isCommandLine = true; -Gamedig.query( - options, - function(state) { - if(outputFormat == 'pretty') { - console.log(JSON.stringify(state,null,' ')); - } else { - console.log(JSON.stringify(state)); - } - } -); +#!/usr/bin/env node + +var argv = require('optimist').argv; + +var debug = argv.debug; +delete argv.debug; +var outputFormat = argv.output; +delete argv.output; + +var options = {}; +for(var key in argv) { + var value = argv[key]; + if( + key == '_' + || key.charAt(0) == '$' + || (typeof value != 'string' && typeof value != 'number') + ) + continue; + options[key] = value; +} + +var Gamedig = require('../lib/index'); +if(debug) Gamedig.debug = true; +Gamedig.isCommandLine = true; +Gamedig.query( + options, + function(state) { + if(outputFormat == 'pretty') { + console.log(JSON.stringify(state,null,' ')); + } else { + console.log(JSON.stringify(state)); + } + } +); diff --git a/bin/genreadme.js b/bin/genreadme.js index 77832f0..81cee99 100644 --- a/bin/genreadme.js +++ b/bin/genreadme.js @@ -1,19 +1,19 @@ -#!/usr/bin/env node - -var fs = require('fs'); - -var TypeResolver = require('../lib/typeresolver'); -var generated = TypeResolver.printReadme(); - -var readmeFilename = __dirname+'/../README.md'; -var readme = fs.readFileSync(readmeFilename, {encoding:'utf8'}); - -var marker_top = ''; -var marker_bottom = ''; - -var start = readme.indexOf(marker_top); -start += marker_top.length; -var end = readme.indexOf(marker_bottom); - -var updated = readme.substr(0,start)+"\n\n"+generated+"\n"+readme.substr(end); -fs.writeFileSync(readmeFilename, updated); +#!/usr/bin/env node + +var fs = require('fs'); + +var TypeResolver = require('../lib/typeresolver'); +var generated = TypeResolver.printReadme(); + +var readmeFilename = __dirname+'/../README.md'; +var readme = fs.readFileSync(readmeFilename, {encoding:'utf8'}); + +var marker_top = ''; +var marker_bottom = ''; + +var start = readme.indexOf(marker_top); +start += marker_top.length; +var end = readme.indexOf(marker_bottom); + +var updated = readme.substr(0,start)+"\n\n"+generated+"\n"+readme.substr(end); +fs.writeFileSync(readmeFilename, updated); diff --git a/games.txt b/games.txt index 26fabe0..c1dc43e 100644 --- a/games.txt +++ b/games.txt @@ -1,273 +1,273 @@ -# id | pretty | protocol | options | parameters - -#### TODO: -# cube1|Cube 1|cube|port=28786,port_query_offset=1 -# assaultcube|Assault Cube|cube|port_query=28764 -# cube2|Cube 2: Sauerbraten|cube|port=28785,port_query_offset=1 -# bloodfrontier|Blood Frontier|cube - -# arcasimracing|Arca Sim Racing|rfactor|port=34397,port_query_offset=-100 -# rfactor|rFactor|rfactor|port=34397,port_query_offset=-100 - -# bfris|BFRIS|bfris|port=44001 -# cs2d|Counter-Strike: 2D|cs2d|port_query=36963 -# freelancer|Freelancer|freelancer|port_query=2302 -# gr|Ghost Recon|ghostrecon|port=2346,port_query_offset=2 -# gtr2|GTR2|gtr2|port=34297,port_query_offset=1 -# haze|Haze|haze -# hexen2|Hexen 2|hexen2|port_query=26900 -# openttd|OpenTTD|openttd|port=3979 -# plainsight|Plain Sight|plainsight -# redfaction|Red Faction|redfaction|port_query=7755 -# savage|Savage|savage|port_query=11235 -# savage2|Savage 2|savage2|port_query=11235 -# teeworlds|Teeworlds|teeworlds|port=8303 -# tribes|Tribes 1: Starsiege|tribes|port_query=28001 -# tribes2|Tribes 2|tribes2|port_query=28000 -# vcmp|Vice City Multiplayer|vcmp -# worldinconflict|World in Conflict|worldinconflict - - - -ageofchivalry|Age of Chivalry|valve -aoe2|Age of Empires 2|ase|port_query=27224 -alienarena|Alien Arena|quake2|port_query=27910 -alienswarm|Alien Swarm|valve -avp2|Aliens vs Predator 2|gamespy1|port=27888 -# avp2010 doesn't really... have a default port or query port -# both port and port_query should be specified when used -avp2010|Aliens vs Predator 2010|valve - -americasarmy|America's Army|americasarmy|port=1716,port_query_offset=1 -americasarmy2|America's Army 2|americasarmy|port=1716,port_query_offset=1 -americasarmy3|America's Army 3|valve|port=8777,port_query=27020 -americasarmypg|America's Army: Proving Grounds|valve|port=8777,port_query=27020 - -arma|ArmA Armed Assault 1|gamespy2|port=2302 -arma2|ArmA Armed Assault 2|valve|port=2302,port_query_offset=1 -arma3|ArmA Armed Assault 3|valve|port=2302,port_query_offset=1 - -armagetron|Armagetron|armagetron|port=4534 -baldursgate|Baldur's Gate|gamespy1|port=6073,port_query=1470 - -bf1942|Battlefield 1942|gamespy1|port=14567,port_query=23000 -bfv|Battlefield Vietnam|gamespy2|port=15567,port_query=23000 -bf2|Battlefield 2|gamespy3|port=16567,port_query=29900|noChallenge -bf2142|Battlefield 2142|gamespy3|port=16567,port_query=29900 -bfbc2|Battlefield: Bad Company 2|battlefield|port=19567,port_query=48888|isBadCompany2 -bf3|Battlefield 3|battlefield|port=25200,port_query_offset=22000 -bf4|Battlefield 4|battlefield|port=25200,port_query_offset=22000 - -breach|Breach|valve|port=27016 -breed|Breed|gamespy2|port=7649 -brink|Brink|valve|port_query_offset=1 -buildandshoot|Build and Shoot|buildandshoot|port=32887,port_query=32886 - -cod|Call of Duty|quake3|port=28960 -coduo|Call of Duty: United Offensive|quake3|port=28960 -cod2|Call of Duty 2|quake3|port=28960 -cod3|Call of Duty 3|quake3|port=28960 -cod4|Call of Duty 4: Modern Warfare|quake3|port=28960 -codwaw|Call of Duty: World at War|quake3|port=28960 -codmw2|Call of Duty: Modern Warfare 2|quake3|port=28960 -codmw3|Call of Duty: Modern Warfare 3|valve|port_query_offset=2 - -callofjuarez|Call of Juarez|ase|port_query=26000 -chaser|Chaser|ase|port=3000,port_query_offset=123 -chrome|Chrome|ase|port=27015,port_query_offset=123 -codenameeagle|Codename Eagle|gamespy1|port_query=4711 -commandos3|Commandos 3: Destination Berlin|gamespy1|port_query=6500 -cacrenegade|Command and Conquer: Renegade|gamespy1|port=4848,port_query=25300 -contactjack|Contact J.A.C.K.|gamespy1|port_query=27888 - -cs16|Counter-Strike 1.6|valve -cscz|Counter-Strike: Condition Zero|valve -css|Counter-Strike: Source|valve -csgo|Counter-Strike: Global Offensive|valve||isCsGo - -crossracing|Cross Racing Championship|ase|port=12321,port_query_offset=123 - -crysis|Crysis|gamespy3|port=64087 -crysiswars|Crysis Wars|gamespy3|port=64100 -crysis2|Crysis 2|gamespy3|port=64000 - -daikatana|Daikatana|quake2|port=27982,port_query_offset=10 -dmomam|Dark Messiah of Might and Magic|valve -darkesthour|Darkest Hour|unreal2|port=7757,port_query_offset=1 -dayz|DayZ|valve|port=2302,port_query_offset=24714|doc_notes=dayz -deadlydozenpt|Deadly Dozen: Pacific Theater|gamespy1|port_query=25300 -dh2005|Deer Hunter 2005|gamespy2|port=23459,port_query=34567 -descent3|Descent 3|gamespy1|port=2092,port_query=20142 -deusex|Deus Ex|gamespy2|port=7791,port_query_offset=1 -devastation|Devastation|unreal2|port=7777,port_query_offset=1 -dinodday|Dino D-Day|valve -dirttrackracing2|Dirt Track Racing 2|gamespy1|port=32240,port_query_offset=-100 -dod|Day of Defeat|valve -dods|Day of Defeat: Source|valve -doom3|Doom 3|doom3|port=27666 -dota2|DOTA 2|valve -drakan|Drakan|gamespy1|port=27045,port_query_offset=1 -etqw|Enemy Territory Quake Wars|doom3|port=3074,port_query=27733|isEtqw,hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag -fear|F.E.A.R.|gamespy2|port_query=27888 -f12002|F1 2002|gamespy1|port_query=3297 -f1c9902|F1 Challenge 99-02|gamespy1|port_query=34397 -farcry|Far Cry|ase|port=49001,port_query_offset=123 -farcry2|Far Cry|ase|port_query=14001 -fortressforever|Fortress Forever|valve -flashpoint|Flashpoint|gamespy1|port=2302,port_query_offset=1 -ffow|Frontlines: Fuel of War|ffow|port=5476,port_query_offset=2 -garrysmod|Garry's Mod|valve -graw|Ghost Recon: Advanced Warfighter|gamespy2|port_query=15250 -graw2|Ghost Recon: Advanced Warfighter 2|gamespy2|port_query=16250 -giantscitizenkabuto|Giants: Citizen Kabuto|gamespy1|port_query=8911 -globaloperations|Global Operations|gamespy1|port_query=28672 -gore|Gore|gamespy1|port=27777,port_query_offset=1 -gunmanchronicles|Gunman Chronicles|valve -hldm|Half-Life 1 Deathmatch|valve -hl2dm|Half-Life 2 Deathmatch|valve -halo|Halo|gamespy2|port=2302 -halo2|Halo 2|gamespy2|port=2302 -heretic2|Heretic 2|gamespy1|port_query=28910 -hexenworld|Hexen World|hexenworld|port_query=26950 -hidden|The Hidden: Source|valve -had2|Hidden and Dangerous 2|gamespy1|port=11001,port_query_offset=3 -homefront|Homefront|valve -homeworld2|Homeworld 2|gamespy1|port_query=6500 -igi2|IGI-2: Covert Strike|gamespy1|port_query=26001 -il2|IL-2 Sturmovik|gamespy1|port_query=21000 -insurgency|Insurgency|valve -ironstorm|Iron Storm|gamespy1|port_query=3505 -jamesbondnightfire|James Bond: Nightfire|gamespy1|port_query=6550 -jc2mp|Just Cause 2 Multiplayer|jc2mp|port=7777 -killingfloor|Killing Floor|killingfloor|port=7707,port_query_offset=1 -kingpin|Kingpin: Life of Crime|gamespy1|port=31510,port_query_offset=-10 -kisspc|KISS Psycho Circus|gamespy1|port=7777,port_query_offset=1 -kzmod|KzMod|valve -left4dead|Left 4 Dead|valve -left4dead2|Left 4 Dead 2|valve -m2mp|Mafia 2 Multiplayer|m2mp|port=27016,port_query_offset=1 - -mohaa|Medal of Honor: Allied Assault|gamespy1|port=12203,port_query_offset=97 -mohpa|Medal of Honor: Pacific Assault|gamespy1|port=13203,port_query_offset=97 -mohab|Medal of Honor: Airborne|gamespy1|port=12203,port_query_offset=97 -mohsh|Medal of Honor: Spearhead|gamespy1|port=12203,port_query_offset=97 -mohbt|Medal of Honor: Breakthrough|gamespy1|port=12203,port_query_offset=97 -moh2010|Medal of Honor 2010|battlefield|port=7673,port_query=48888 -mohwf|Medal of Honor: Warfighter|battlefield|port=25200,port_query_offset=22000 - -minecraft|Minecraft|gamespy3|port=25565|maxAttempts=2,srvRecord=_minecraft._tcp,doc_notes=minecraft -minecraftping|Minecraft|minecraftping|port=25565|srvRecord=_minecraft._tcp,doc_notes=minecraft -mnc|Monday Night Combat|valve|port=7777,port_query=27016 -mtavc|Multi Theft Auto: Vice City|ase|port=22003,port_query_offset=123 -mtasa|Multi Theft Auto: San Andreas|ase|port=22003,port_query_offset=123 -mumble|Mumble|mumble|port=64738,port_query=27800|doc_notes=mumble -mumbleping|Mumble|mumbleping|port=64738|doc_notes=mumble -mutantfactions|Mutant Factions|mutantfactions|port=11235 -nascarthunder2004|Nascar Thunder 2004|gamespy2|port_query=13333 -netpanzer|netPanzer|gamespy1|3030 -nmrih|No More Room in Hell|valve -ns|Natural Selection|valve -ns2|Natural Selection 2|valve|port_query_offset=1 -nfshp2|Need for Speed: Hot Pursuit 2|gamespy1|port_query=61220 -nab|Nerf Arena Blast|gamespy1|port=4444,port_query_offset=1 -nwn|Neverwinter Nights|gamespy2|port_query=5121 -nwn2|Neverwinter Nights 2|gamespy2|port=5121,port_query=6500 -nexuiz|Nexuiz|quake3|port_query=26000 -nitrofamily|Nitro Family|gamespy1|port_query=25601 -nolf|No One Lives Forever|gamespy1|port_query=27888 -nolf2|No One Lives Forever 2|gamespy1|port_query=27890 -nucleardawn|Nuclear Dawn|valve -openarena|OpenArena|quake3|port_query=27960 -operationflashpoint|Operation Flashpoint|gamespy1|port=2234,port_query_offset=1 -painkiller|Painkiller|ase|port=3455,port_query_offset=123 -postal2|Postal 2|gamespy1|port=7777,port_query_offset=1 -prey|Prey|doom3|port_query=27719 - -quake1|Quake 1: QuakeWorld|quake1|port=27500 -quake2|Quake 2|quake2|port=27910 -quake3|Quake 3: Arena|quake3|port=27960 -quake4|Quake 4|doom3|port=28004|hasClanTag - -ragdollkungfu|Rag Doll Kung Fu|valve - -r6|Rainbow Six|gamespy1|port_query=2348 -r6roguespear|Rainbow Six 2: Rogue Spear|gamespy1|port_query=2346 -r6ravenshield|Rainbow Six 3: Raven Shield|gamespy1|port=7777,port_query_offset=1000 - -rallisportchallenge|RalliSport Challenge|gamespy1|port_query=17500 -rallymasters|Rally Masters|gamespy1|port_query=16666 -redorchestra|Red Orchestra|unreal2|port=7758,port_query_offset=1 -redorchestraost|Red Orchestra: Ostfront 41-45|gamespy1|port=7757,port_query_offset=10 -redorchestra2|Red Orchestra 2|valve|port=7777,port_query=27015 -redline|Redline|gamespy1|port_query=25252 -rtcw|Return to Castle Wolfenstein|quake3|port_query=27960 -ricochet|Ricochet|valve -riseofnations|Rise of Nations|gamespy1|port_query=6501 -rune|Rune|gamespy1|port=7777,port_query_offset=1 -rust|Rust|valve|port=28015,port_query_offset=1 -samp|San Andreas Multiplayer|samp|port=7777 -ss|Serious Sam|gamespy1|port=25600,port_query_offset=1 -ss2|Serious Sam 2|gamespy2|port=25600 -shatteredhorizon|Shattered Horizon|valve -ship|The Ship|valve -shogo|Shogo|gamespy1|port_query=27888 -shootmania|Shootmania|nadeo||doc_notes=nadeo-shootmania--trackmania--etc -sin|SiN|gamespy1|port_query=22450 -sinep|SiN Episodes|valve -soldat|Soldat|ase|port=13073,port_query_offset=123 -sof|Soldier of Fortune|quake1|port_query=28910 -sof2|Soldier of Fortune 2|quake3|port_query=20100 -stalker|S.T.A.L.K.E.R.|gamespy3|port=5445,port_query_offset=2 - -stbc|Star Trek: Bridge Commander|gamespy1|port_query=22101 -stvef|Star Trek: Voyager - Elite Force|quake3|port_query=27960 -stvef2|Star Trek: Voyager - Elite Force 2|quake3|port_query=29253 - -swbf|Star Wars: Battlefront|gamespy2|port_query=3658 -swbf2|Star Wars: Battlefront 2|gamespy2|port_query=3658 -swjk|Star Wars: Jedi Knight|quake3|port_query=29070 -swjk2|Star Wars: Jedi Knight 2|quake3|port_query=28070 -swrc|Star Wars: Republic Commando|gamespy2|port=7777,port_query=11138 - -starbound|Starbound|valve|port=21025 -suicidesurvival|Suicide Survival|valve -swat4|SWAT 4|gamespy2|port=10480,port_query_offset=2 -svencoop|Sven Coop|valve -synergy|Synergy|valve -tacticalops|Tactical Ops|gamespy1|port=7777,port_query_offset=1 -teamfactor|Team Factor|gamespy1|port_query=57778 -tfc|Team Fortress Classic|valve -tf2|Team Fortress 2|valve -teamspeak2|Teamspeak 2|teamspeak2|port=8767,port_query=51234 -teamspeak3|Teamspeak 3|teamspeak3|port=9987,port_query=10011 -terminus|Terminus|gamespy1|port_query=12286 -terraria|Terraria|terraria|port=7777,port_query_offset=101|doc_notes=terraria -thps3|Tony Hawk's Pro Skater 3|gamespy1|port_query=6500 -thps4|Tony Hawk's Pro Skater 4|gamespy1|port_query=6500 -thu2|Tony Hawk's Underground 2|gamespy1|port_query=5153 -trackmania2|Trackmania 2|nadeo||doc_notes=nadeo-shootmania--trackmania--etc -trackmaniaforever|Trackmania Forever|nadeo||doc_notes=nadeo-shootmania--trackmania--etc -tremulous|Tremulous|quake3|port_query=30720 -tribesvengeance|Tribes: Vengeance|gamespy2|port=7777,port_query_offset=1 -tron20|Tron 2.0|gamespy2|port_query=27888 -turok2|Turok 2|gamespy1|port_query=12880 -universalcombat|Universal Combat|ase|port=1135,port_query_offset=123 - -unreal|Unreal|gamespy1|port=7777,port_query_offset=1 -ut|Unreal Tournament|gamespy1|port=7777,port_query_offset=1 -ut2003|Unreal Tournament 2003|unreal2|port=7757,port_query_offset=1 -ut2004|Unreal Tournament 2004|ut2004|port=7777,port_query_offset=1 -ut3|Unreal Tournament 3|ut3|port=7777,port_query_offset=-1277 - -urbanterror|Urban Terror|quake3|port_query=27960 -v8supercar|V8 Supercar Challenge|gamespy1|port_query=16700 -ventrilo|Ventrilo|ventrilo|port=3784 -vietcong|Vietcong|gamespy1|port=5425,port_query=15425 -vietcong2|Vietcong 2|gamespy2|port=5001,port_query=19967 -warsow|Warsow|warsow|port=44400 -wheeloftime|Wheel of Time|gamespy1|port=7777,port_query_offset=1 -wolfenstein2009|Wolfenstein 2009|doom3|port_query=27666|hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag -wolfensteinet|Wolfenstein: Enemy Territory|quake3|port_query=27960 -xpandrally|Xpand Rally|ase|port=28015,port_query_offset=123 -zombiemaster|Zombie Master|valve -zps|Zombie Panic: Source|valve +# id | pretty | protocol | options | parameters + +#### TODO: +# cube1|Cube 1|cube|port=28786,port_query_offset=1 +# assaultcube|Assault Cube|cube|port_query=28764 +# cube2|Cube 2: Sauerbraten|cube|port=28785,port_query_offset=1 +# bloodfrontier|Blood Frontier|cube + +# arcasimracing|Arca Sim Racing|rfactor|port=34397,port_query_offset=-100 +# rfactor|rFactor|rfactor|port=34397,port_query_offset=-100 + +# bfris|BFRIS|bfris|port=44001 +# cs2d|Counter-Strike: 2D|cs2d|port_query=36963 +# freelancer|Freelancer|freelancer|port_query=2302 +# gr|Ghost Recon|ghostrecon|port=2346,port_query_offset=2 +# gtr2|GTR2|gtr2|port=34297,port_query_offset=1 +# haze|Haze|haze +# hexen2|Hexen 2|hexen2|port_query=26900 +# openttd|OpenTTD|openttd|port=3979 +# plainsight|Plain Sight|plainsight +# redfaction|Red Faction|redfaction|port_query=7755 +# savage|Savage|savage|port_query=11235 +# savage2|Savage 2|savage2|port_query=11235 +# teeworlds|Teeworlds|teeworlds|port=8303 +# tribes|Tribes 1: Starsiege|tribes|port_query=28001 +# tribes2|Tribes 2|tribes2|port_query=28000 +# vcmp|Vice City Multiplayer|vcmp +# worldinconflict|World in Conflict|worldinconflict + + + +ageofchivalry|Age of Chivalry|valve +aoe2|Age of Empires 2|ase|port_query=27224 +alienarena|Alien Arena|quake2|port_query=27910 +alienswarm|Alien Swarm|valve +avp2|Aliens vs Predator 2|gamespy1|port=27888 +# avp2010 doesn't really... have a default port or query port +# both port and port_query should be specified when used +avp2010|Aliens vs Predator 2010|valve + +americasarmy|America's Army|americasarmy|port=1716,port_query_offset=1 +americasarmy2|America's Army 2|americasarmy|port=1716,port_query_offset=1 +americasarmy3|America's Army 3|valve|port=8777,port_query=27020 +americasarmypg|America's Army: Proving Grounds|valve|port=8777,port_query=27020 + +arma|ArmA Armed Assault 1|gamespy2|port=2302 +arma2|ArmA Armed Assault 2|valve|port=2302,port_query_offset=1 +arma3|ArmA Armed Assault 3|valve|port=2302,port_query_offset=1 + +armagetron|Armagetron|armagetron|port=4534 +baldursgate|Baldur's Gate|gamespy1|port=6073,port_query=1470 + +bf1942|Battlefield 1942|gamespy1|port=14567,port_query=23000 +bfv|Battlefield Vietnam|gamespy2|port=15567,port_query=23000 +bf2|Battlefield 2|gamespy3|port=16567,port_query=29900|noChallenge +bf2142|Battlefield 2142|gamespy3|port=16567,port_query=29900 +bfbc2|Battlefield: Bad Company 2|battlefield|port=19567,port_query=48888|isBadCompany2 +bf3|Battlefield 3|battlefield|port=25200,port_query_offset=22000 +bf4|Battlefield 4|battlefield|port=25200,port_query_offset=22000 + +breach|Breach|valve|port=27016 +breed|Breed|gamespy2|port=7649 +brink|Brink|valve|port_query_offset=1 +buildandshoot|Build and Shoot|buildandshoot|port=32887,port_query=32886 + +cod|Call of Duty|quake3|port=28960 +coduo|Call of Duty: United Offensive|quake3|port=28960 +cod2|Call of Duty 2|quake3|port=28960 +cod3|Call of Duty 3|quake3|port=28960 +cod4|Call of Duty 4: Modern Warfare|quake3|port=28960 +codwaw|Call of Duty: World at War|quake3|port=28960 +codmw2|Call of Duty: Modern Warfare 2|quake3|port=28960 +codmw3|Call of Duty: Modern Warfare 3|valve|port_query_offset=2 + +callofjuarez|Call of Juarez|ase|port_query=26000 +chaser|Chaser|ase|port=3000,port_query_offset=123 +chrome|Chrome|ase|port=27015,port_query_offset=123 +codenameeagle|Codename Eagle|gamespy1|port_query=4711 +commandos3|Commandos 3: Destination Berlin|gamespy1|port_query=6500 +cacrenegade|Command and Conquer: Renegade|gamespy1|port=4848,port_query=25300 +contactjack|Contact J.A.C.K.|gamespy1|port_query=27888 + +cs16|Counter-Strike 1.6|valve +cscz|Counter-Strike: Condition Zero|valve +css|Counter-Strike: Source|valve +csgo|Counter-Strike: Global Offensive|valve||isCsGo + +crossracing|Cross Racing Championship|ase|port=12321,port_query_offset=123 + +crysis|Crysis|gamespy3|port=64087 +crysiswars|Crysis Wars|gamespy3|port=64100 +crysis2|Crysis 2|gamespy3|port=64000 + +daikatana|Daikatana|quake2|port=27982,port_query_offset=10 +dmomam|Dark Messiah of Might and Magic|valve +darkesthour|Darkest Hour|unreal2|port=7757,port_query_offset=1 +dayz|DayZ|valve|port=2302,port_query_offset=24714|doc_notes=dayz +deadlydozenpt|Deadly Dozen: Pacific Theater|gamespy1|port_query=25300 +dh2005|Deer Hunter 2005|gamespy2|port=23459,port_query=34567 +descent3|Descent 3|gamespy1|port=2092,port_query=20142 +deusex|Deus Ex|gamespy2|port=7791,port_query_offset=1 +devastation|Devastation|unreal2|port=7777,port_query_offset=1 +dinodday|Dino D-Day|valve +dirttrackracing2|Dirt Track Racing 2|gamespy1|port=32240,port_query_offset=-100 +dod|Day of Defeat|valve +dods|Day of Defeat: Source|valve +doom3|Doom 3|doom3|port=27666 +dota2|DOTA 2|valve +drakan|Drakan|gamespy1|port=27045,port_query_offset=1 +etqw|Enemy Territory Quake Wars|doom3|port=3074,port_query=27733|isEtqw,hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag +fear|F.E.A.R.|gamespy2|port_query=27888 +f12002|F1 2002|gamespy1|port_query=3297 +f1c9902|F1 Challenge 99-02|gamespy1|port_query=34397 +farcry|Far Cry|ase|port=49001,port_query_offset=123 +farcry2|Far Cry|ase|port_query=14001 +fortressforever|Fortress Forever|valve +flashpoint|Flashpoint|gamespy1|port=2302,port_query_offset=1 +ffow|Frontlines: Fuel of War|ffow|port=5476,port_query_offset=2 +garrysmod|Garry's Mod|valve +graw|Ghost Recon: Advanced Warfighter|gamespy2|port_query=15250 +graw2|Ghost Recon: Advanced Warfighter 2|gamespy2|port_query=16250 +giantscitizenkabuto|Giants: Citizen Kabuto|gamespy1|port_query=8911 +globaloperations|Global Operations|gamespy1|port_query=28672 +gore|Gore|gamespy1|port=27777,port_query_offset=1 +gunmanchronicles|Gunman Chronicles|valve +hldm|Half-Life 1 Deathmatch|valve +hl2dm|Half-Life 2 Deathmatch|valve +halo|Halo|gamespy2|port=2302 +halo2|Halo 2|gamespy2|port=2302 +heretic2|Heretic 2|gamespy1|port_query=28910 +hexenworld|Hexen World|hexenworld|port_query=26950 +hidden|The Hidden: Source|valve +had2|Hidden and Dangerous 2|gamespy1|port=11001,port_query_offset=3 +homefront|Homefront|valve +homeworld2|Homeworld 2|gamespy1|port_query=6500 +igi2|IGI-2: Covert Strike|gamespy1|port_query=26001 +il2|IL-2 Sturmovik|gamespy1|port_query=21000 +insurgency|Insurgency|valve +ironstorm|Iron Storm|gamespy1|port_query=3505 +jamesbondnightfire|James Bond: Nightfire|gamespy1|port_query=6550 +jc2mp|Just Cause 2 Multiplayer|jc2mp|port=7777 +killingfloor|Killing Floor|killingfloor|port=7707,port_query_offset=1 +kingpin|Kingpin: Life of Crime|gamespy1|port=31510,port_query_offset=-10 +kisspc|KISS Psycho Circus|gamespy1|port=7777,port_query_offset=1 +kzmod|KzMod|valve +left4dead|Left 4 Dead|valve +left4dead2|Left 4 Dead 2|valve +m2mp|Mafia 2 Multiplayer|m2mp|port=27016,port_query_offset=1 + +mohaa|Medal of Honor: Allied Assault|gamespy1|port=12203,port_query_offset=97 +mohpa|Medal of Honor: Pacific Assault|gamespy1|port=13203,port_query_offset=97 +mohab|Medal of Honor: Airborne|gamespy1|port=12203,port_query_offset=97 +mohsh|Medal of Honor: Spearhead|gamespy1|port=12203,port_query_offset=97 +mohbt|Medal of Honor: Breakthrough|gamespy1|port=12203,port_query_offset=97 +moh2010|Medal of Honor 2010|battlefield|port=7673,port_query=48888 +mohwf|Medal of Honor: Warfighter|battlefield|port=25200,port_query_offset=22000 + +minecraft|Minecraft|gamespy3|port=25565|maxAttempts=2,srvRecord=_minecraft._tcp,doc_notes=minecraft +minecraftping|Minecraft|minecraftping|port=25565|srvRecord=_minecraft._tcp,doc_notes=minecraft +mnc|Monday Night Combat|valve|port=7777,port_query=27016 +mtavc|Multi Theft Auto: Vice City|ase|port=22003,port_query_offset=123 +mtasa|Multi Theft Auto: San Andreas|ase|port=22003,port_query_offset=123 +mumble|Mumble|mumble|port=64738,port_query=27800|doc_notes=mumble +mumbleping|Mumble|mumbleping|port=64738|doc_notes=mumble +mutantfactions|Mutant Factions|mutantfactions|port=11235 +nascarthunder2004|Nascar Thunder 2004|gamespy2|port_query=13333 +netpanzer|netPanzer|gamespy1|3030 +nmrih|No More Room in Hell|valve +ns|Natural Selection|valve +ns2|Natural Selection 2|valve|port_query_offset=1 +nfshp2|Need for Speed: Hot Pursuit 2|gamespy1|port_query=61220 +nab|Nerf Arena Blast|gamespy1|port=4444,port_query_offset=1 +nwn|Neverwinter Nights|gamespy2|port_query=5121 +nwn2|Neverwinter Nights 2|gamespy2|port=5121,port_query=6500 +nexuiz|Nexuiz|quake3|port_query=26000 +nitrofamily|Nitro Family|gamespy1|port_query=25601 +nolf|No One Lives Forever|gamespy1|port_query=27888 +nolf2|No One Lives Forever 2|gamespy1|port_query=27890 +nucleardawn|Nuclear Dawn|valve +openarena|OpenArena|quake3|port_query=27960 +operationflashpoint|Operation Flashpoint|gamespy1|port=2234,port_query_offset=1 +painkiller|Painkiller|ase|port=3455,port_query_offset=123 +postal2|Postal 2|gamespy1|port=7777,port_query_offset=1 +prey|Prey|doom3|port_query=27719 + +quake1|Quake 1: QuakeWorld|quake1|port=27500 +quake2|Quake 2|quake2|port=27910 +quake3|Quake 3: Arena|quake3|port=27960 +quake4|Quake 4|doom3|port=28004|hasClanTag + +ragdollkungfu|Rag Doll Kung Fu|valve + +r6|Rainbow Six|gamespy1|port_query=2348 +r6roguespear|Rainbow Six 2: Rogue Spear|gamespy1|port_query=2346 +r6ravenshield|Rainbow Six 3: Raven Shield|gamespy1|port=7777,port_query_offset=1000 + +rallisportchallenge|RalliSport Challenge|gamespy1|port_query=17500 +rallymasters|Rally Masters|gamespy1|port_query=16666 +redorchestra|Red Orchestra|unreal2|port=7758,port_query_offset=1 +redorchestraost|Red Orchestra: Ostfront 41-45|gamespy1|port=7757,port_query_offset=10 +redorchestra2|Red Orchestra 2|valve|port=7777,port_query=27015 +redline|Redline|gamespy1|port_query=25252 +rtcw|Return to Castle Wolfenstein|quake3|port_query=27960 +ricochet|Ricochet|valve +riseofnations|Rise of Nations|gamespy1|port_query=6501 +rune|Rune|gamespy1|port=7777,port_query_offset=1 +rust|Rust|valve|port=28015,port_query_offset=1 +samp|San Andreas Multiplayer|samp|port=7777 +ss|Serious Sam|gamespy1|port=25600,port_query_offset=1 +ss2|Serious Sam 2|gamespy2|port=25600 +shatteredhorizon|Shattered Horizon|valve +ship|The Ship|valve +shogo|Shogo|gamespy1|port_query=27888 +shootmania|Shootmania|nadeo||doc_notes=nadeo-shootmania--trackmania--etc +sin|SiN|gamespy1|port_query=22450 +sinep|SiN Episodes|valve +soldat|Soldat|ase|port=13073,port_query_offset=123 +sof|Soldier of Fortune|quake1|port_query=28910 +sof2|Soldier of Fortune 2|quake3|port_query=20100 +stalker|S.T.A.L.K.E.R.|gamespy3|port=5445,port_query_offset=2 + +stbc|Star Trek: Bridge Commander|gamespy1|port_query=22101 +stvef|Star Trek: Voyager - Elite Force|quake3|port_query=27960 +stvef2|Star Trek: Voyager - Elite Force 2|quake3|port_query=29253 + +swbf|Star Wars: Battlefront|gamespy2|port_query=3658 +swbf2|Star Wars: Battlefront 2|gamespy2|port_query=3658 +swjk|Star Wars: Jedi Knight|quake3|port_query=29070 +swjk2|Star Wars: Jedi Knight 2|quake3|port_query=28070 +swrc|Star Wars: Republic Commando|gamespy2|port=7777,port_query=11138 + +starbound|Starbound|valve|port=21025 +suicidesurvival|Suicide Survival|valve +swat4|SWAT 4|gamespy2|port=10480,port_query_offset=2 +svencoop|Sven Coop|valve +synergy|Synergy|valve +tacticalops|Tactical Ops|gamespy1|port=7777,port_query_offset=1 +teamfactor|Team Factor|gamespy1|port_query=57778 +tfc|Team Fortress Classic|valve +tf2|Team Fortress 2|valve +teamspeak2|Teamspeak 2|teamspeak2|port=8767,port_query=51234 +teamspeak3|Teamspeak 3|teamspeak3|port=9987,port_query=10011 +terminus|Terminus|gamespy1|port_query=12286 +terraria|Terraria|terraria|port=7777,port_query_offset=101|doc_notes=terraria +thps3|Tony Hawk's Pro Skater 3|gamespy1|port_query=6500 +thps4|Tony Hawk's Pro Skater 4|gamespy1|port_query=6500 +thu2|Tony Hawk's Underground 2|gamespy1|port_query=5153 +trackmania2|Trackmania 2|nadeo||doc_notes=nadeo-shootmania--trackmania--etc +trackmaniaforever|Trackmania Forever|nadeo||doc_notes=nadeo-shootmania--trackmania--etc +tremulous|Tremulous|quake3|port_query=30720 +tribesvengeance|Tribes: Vengeance|gamespy2|port=7777,port_query_offset=1 +tron20|Tron 2.0|gamespy2|port_query=27888 +turok2|Turok 2|gamespy1|port_query=12880 +universalcombat|Universal Combat|ase|port=1135,port_query_offset=123 + +unreal|Unreal|gamespy1|port=7777,port_query_offset=1 +ut|Unreal Tournament|gamespy1|port=7777,port_query_offset=1 +ut2003|Unreal Tournament 2003|unreal2|port=7757,port_query_offset=1 +ut2004|Unreal Tournament 2004|ut2004|port=7777,port_query_offset=1 +ut3|Unreal Tournament 3|ut3|port=7777,port_query_offset=-1277 + +urbanterror|Urban Terror|quake3|port_query=27960 +v8supercar|V8 Supercar Challenge|gamespy1|port_query=16700 +ventrilo|Ventrilo|ventrilo|port=3784 +vietcong|Vietcong|gamespy1|port=5425,port_query=15425 +vietcong2|Vietcong 2|gamespy2|port=5001,port_query=19967 +warsow|Warsow|warsow|port=44400 +wheeloftime|Wheel of Time|gamespy1|port=7777,port_query_offset=1 +wolfenstein2009|Wolfenstein 2009|doom3|port_query=27666|hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag +wolfensteinet|Wolfenstein: Enemy Territory|quake3|port_query=27960 +xpandrally|Xpand Rally|ase|port=28015,port_query_offset=123 +zombiemaster|Zombie Master|valve +zps|Zombie Panic: Source|valve diff --git a/lib/Class.js b/lib/Class.js index 1bfd518..6e8efbc 100644 --- a/lib/Class.js +++ b/lib/Class.js @@ -1,74 +1,74 @@ -/* based on Simple JavaScript Inheritance -* By John Resig http://ejohn.org/ -* MIT Licensed. -*/ - -var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; - -// The base Class implementation (does nothing) -var Class = function(){}; - -// Create a new Class that inherits from this class -Class.extend = function() { - var args = Array.prototype.slice.call(arguments); - var name = 'Class'; - var parent = this; - var prop = {}; - if(typeof args[0] == 'string') name = args.shift(); - if(args.length >= 2) parent = args.shift(); - prop = args.shift(); - - // Copy prototype from the parent object - var prototype = {}; - for(var name in parent.prototype) { - prototype[name] = parent.prototype[name]; - } - - // Copy the properties over onto the new prototype - for(var name in prop) { - if(typeof prop[name] == "function" && fnTest.test(prop[name])) { - // this is a function that references _super, so we have to wrap it - // and provide it with its super function - prototype[name] = (function(name, fn){ - return function() { - var tmp = this._super; - - // Add a new ._super() method that is the same method - // but on the super-class - if(typeof parent.prototype[name] == 'undefined') { - if(name == 'init') this._super = parent.prototype.constructor; - else this._super = function() { throw new Error('Called _super in method without a parent'); } - } else this._super = parent.prototype[name]; - - // The method only need to be bound temporarily, so we - // remove it when we're done executing - var ret = fn.apply(this, arguments); - this._super = tmp; - - return ret; - }; - })(name, prop[name]); - } else { - prototype[name] = prop[name]; - } - } - - // The dummy class constructor - function Class() { - // All construction is actually done in the init method - if(this.init) this.init.apply(this, arguments); - } - - // Populate our constructed prototype object - Class.prototype = prototype; - - // Enforce the constructor to be what we expect - Class.prototype.constructor = Class; - - // And make this class extendable - Class.extend = arguments.callee; - - return Class; -}; - -module.exports = Class; +/* based on Simple JavaScript Inheritance +* By John Resig http://ejohn.org/ +* MIT Licensed. +*/ + +var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; + +// The base Class implementation (does nothing) +var Class = function(){}; + +// Create a new Class that inherits from this class +Class.extend = function() { + var args = Array.prototype.slice.call(arguments); + var name = 'Class'; + var parent = this; + var prop = {}; + if(typeof args[0] == 'string') name = args.shift(); + if(args.length >= 2) parent = args.shift(); + prop = args.shift(); + + // Copy prototype from the parent object + var prototype = {}; + for(var name in parent.prototype) { + prototype[name] = parent.prototype[name]; + } + + // Copy the properties over onto the new prototype + for(var name in prop) { + if(typeof prop[name] == "function" && fnTest.test(prop[name])) { + // this is a function that references _super, so we have to wrap it + // and provide it with its super function + prototype[name] = (function(name, fn){ + return function() { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + if(typeof parent.prototype[name] == 'undefined') { + if(name == 'init') this._super = parent.prototype.constructor; + else this._super = function() { throw new Error('Called _super in method without a parent'); } + } else this._super = parent.prototype[name]; + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, prop[name]); + } else { + prototype[name] = prop[name]; + } + } + + // The dummy class constructor + function Class() { + // All construction is actually done in the init method + if(this.init) this.init.apply(this, arguments); + } + + // Populate our constructed prototype object + Class.prototype = prototype; + + // Enforce the constructor to be what we expect + Class.prototype.constructor = Class; + + // And make this class extendable + Class.extend = arguments.callee; + + return Class; +}; + +module.exports = Class; diff --git a/lib/index.js b/lib/index.js index 79dd1c5..66c6846 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,80 +1,80 @@ -var dgram = require('dgram'), - EventEmitter = require('events').EventEmitter, - util = require('util'), - dns = require('dns'), - TypeResolver = require('./typeresolver'); - -var activeQueries = []; - -var udpSocket = dgram.createSocket('udp4'); -udpSocket.unref(); -udpSocket.bind(21943); -udpSocket.on('message', function(buffer, rinfo) { - if(Gamedig.debug) console.log(rinfo.address+':'+rinfo.port+" <--UDP "+buffer.toString('hex')); - for(var i = 0; i < activeQueries.length; i++) { - var query = activeQueries[i]; - if( - query.options.address != rinfo.address - && query.options.altaddress != rinfo.address - ) continue; - if(query.options.port_query != rinfo.port) continue; - query._udpResponse(buffer); - break; - } -}); -udpSocket.on('error', function(e) { - if(Gamedig.debug) console.log("UDP ERROR: "+e); -}); - -Gamedig = { - - query: function(options,callback) { - if(callback) options.callback = callback; - - var query; - try { - query = TypeResolver.lookup(options.type); - } catch(e) { - process.nextTick(function() { - callback({error:e.message}); - }); - return; - } - query.debug = Gamedig.debug; - query.udpSocket = udpSocket; - query.type = options.type; - - if(!('port' in query.options) && ('port_query' in query.options)) { - if(Gamedig.isCommandLine) { - process.stderr.write( - "Warning! This game is so old, that we don't know" - +" what the server's connection port is. We've guessed that" - +" the query port for "+query.type+" is "+query.options.port_query+"." - +" If you know the connection port for this type of server, please let" - +" us know on the GameDig issue tracker, thanks!\n" - ); - } - query.options.port = query.options.port_query; - delete query.options.port_query; - } - - // copy over options - for(var i in options) query.options[i] = options[i]; - - activeQueries.push(query); - - query.on('finished',function(state) { - var i = activeQueries.indexOf(query); - if(i >= 0) activeQueries.splice(i, 1); - }); - - process.nextTick(function() { - query.start(); - }); - - return query; - } - -}; - -module.exports = Gamedig; +var dgram = require('dgram'), + EventEmitter = require('events').EventEmitter, + util = require('util'), + dns = require('dns'), + TypeResolver = require('./typeresolver'); + +var activeQueries = []; + +var udpSocket = dgram.createSocket('udp4'); +udpSocket.unref(); +udpSocket.bind(21943); +udpSocket.on('message', function(buffer, rinfo) { + if(Gamedig.debug) console.log(rinfo.address+':'+rinfo.port+" <--UDP "+buffer.toString('hex')); + for(var i = 0; i < activeQueries.length; i++) { + var query = activeQueries[i]; + if( + query.options.address != rinfo.address + && query.options.altaddress != rinfo.address + ) continue; + if(query.options.port_query != rinfo.port) continue; + query._udpResponse(buffer); + break; + } +}); +udpSocket.on('error', function(e) { + if(Gamedig.debug) console.log("UDP ERROR: "+e); +}); + +Gamedig = { + + query: function(options,callback) { + if(callback) options.callback = callback; + + var query; + try { + query = TypeResolver.lookup(options.type); + } catch(e) { + process.nextTick(function() { + callback({error:e.message}); + }); + return; + } + query.debug = Gamedig.debug; + query.udpSocket = udpSocket; + query.type = options.type; + + if(!('port' in query.options) && ('port_query' in query.options)) { + if(Gamedig.isCommandLine) { + process.stderr.write( + "Warning! This game is so old, that we don't know" + +" what the server's connection port is. We've guessed that" + +" the query port for "+query.type+" is "+query.options.port_query+"." + +" If you know the connection port for this type of server, please let" + +" us know on the GameDig issue tracker, thanks!\n" + ); + } + query.options.port = query.options.port_query; + delete query.options.port_query; + } + + // copy over options + for(var i in options) query.options[i] = options[i]; + + activeQueries.push(query); + + query.on('finished',function(state) { + var i = activeQueries.indexOf(query); + if(i >= 0) activeQueries.splice(i, 1); + }); + + process.nextTick(function() { + query.start(); + }); + + return query; + } + +}; + +module.exports = Gamedig; diff --git a/lib/reader.js b/lib/reader.js index f86ee13..4046c57 100644 --- a/lib/reader.js +++ b/lib/reader.js @@ -1,124 +1,124 @@ -var Iconv = require('iconv-lite'), - Long = require('long'); - -function readUInt64BE(buffer,offset) { - var high = buffer.readUInt32BE(offset); - var low = buffer.readUInt32BE(offset+4); - return new Long(low,high,true); -} -function readUInt64LE(buffer,offset) { - var low = buffer.readUInt32LE(offset); - var high = buffer.readUInt32LE(offset+4); - return new Long(low,high,true); -} - -function Reader(query,buffer) { - this.query = query; - this.buffer = buffer; - this.i = 0; -} - -Reader.prototype = { - offset: function() { return this.i; }, - skip: function(i) { this.i += i; }, - string: function() { - var args = Array.prototype.slice.call(arguments); - var options = {}; - if(args.length == 0) { - options = {}; - } else if(args.length == 1) { - if(typeof args[0] == 'string') options = { delimiter: args[0] }; - else if(typeof args[0] == 'number') options = { length: args[0] }; - else options = args[0]; - } - - options.encoding = options.encoding || this.query.encoding; - if(options.encoding == 'latin1') options.encoding = 'win1252'; - - var start = this.i+0; - var end = start; - if(!('length' in options)) { - // terminated by the delimiter - var delim = options.delimiter || this.query.delimiter; - if(typeof delim == 'string') delim = delim.charCodeAt(0); - while(true) { - if(end >= this.buffer.length) { - end = this.buffer.length; - break; - } - if(this.buffer.readUInt8(end) == delim) break; - end++; - } - this.i = end+1; - } else { - end = start+options.length; - if(end >= this.buffer.length) { - end = this.buffer.length; - } - this.i = end; - } - - var out = this.buffer.slice(start, end); - var enc = options.encoding; - if(enc == 'utf8' || enc == 'ucs2' || enc == 'binary') { - out = out.toString(enc); - } else { - out = Iconv.decode(out,enc); - } - return out; - }, - int: function(bytes) { - var r = 0; - if(this.remaining() >= bytes) { - if(this.query.byteorder == 'be') { - if(bytes == 1) r = this.buffer.readInt8(this.i); - else if(bytes == 2) r = this.buffer.readInt16BE(this.i); - else if(bytes == 4) r = this.buffer.readInt32BE(this.i); - } else { - if(bytes == 1) r = this.buffer.readInt8(this.i); - else if(bytes == 2) r = this.buffer.readInt16LE(this.i); - else if(bytes == 4) r = this.buffer.readInt32LE(this.i); - } - } - this.i += bytes; - return r; - }, - uint: function(bytes) { - var r = 0; - if(this.remaining() >= bytes) { - if(this.query.byteorder == 'be') { - if(bytes == 1) r = this.buffer.readUInt8(this.i); - else if(bytes == 2) r = this.buffer.readUInt16BE(this.i); - else if(bytes == 4) r = this.buffer.readUInt32BE(this.i); - else if(bytes == 8) r = readUInt64BE(this.buffer,this.i).toString(); - } else { - if(bytes == 1) r = this.buffer.readUInt8(this.i); - else if(bytes == 2) r = this.buffer.readUInt16LE(this.i); - else if(bytes == 4) r = this.buffer.readUInt32LE(this.i); - else if(bytes == 8) r = readUInt64LE(this.buffer,this.i).toString(); - } - } - this.i += bytes; - return r; - }, - float: function() { - var r = 0; - if(this.remaining() >= 4) { - if(this.query.byteorder == 'be') r = this.buffer.readFloatBE(this.i); - else r = this.buffer.readFloatLE(this.i); - } - this.i += 4; - return r; - }, - remaining: function() { - return this.buffer.length-this.i; - }, - rest: function() { - return this.buffer.slice(this.i); - }, - done: function() { - return this.i >= this.buffer.length; - } -}; - -module.exports = Reader; +var Iconv = require('iconv-lite'), + Long = require('long'); + +function readUInt64BE(buffer,offset) { + var high = buffer.readUInt32BE(offset); + var low = buffer.readUInt32BE(offset+4); + return new Long(low,high,true); +} +function readUInt64LE(buffer,offset) { + var low = buffer.readUInt32LE(offset); + var high = buffer.readUInt32LE(offset+4); + return new Long(low,high,true); +} + +function Reader(query,buffer) { + this.query = query; + this.buffer = buffer; + this.i = 0; +} + +Reader.prototype = { + offset: function() { return this.i; }, + skip: function(i) { this.i += i; }, + string: function() { + var args = Array.prototype.slice.call(arguments); + var options = {}; + if(args.length == 0) { + options = {}; + } else if(args.length == 1) { + if(typeof args[0] == 'string') options = { delimiter: args[0] }; + else if(typeof args[0] == 'number') options = { length: args[0] }; + else options = args[0]; + } + + options.encoding = options.encoding || this.query.encoding; + if(options.encoding == 'latin1') options.encoding = 'win1252'; + + var start = this.i+0; + var end = start; + if(!('length' in options)) { + // terminated by the delimiter + var delim = options.delimiter || this.query.delimiter; + if(typeof delim == 'string') delim = delim.charCodeAt(0); + while(true) { + if(end >= this.buffer.length) { + end = this.buffer.length; + break; + } + if(this.buffer.readUInt8(end) == delim) break; + end++; + } + this.i = end+1; + } else { + end = start+options.length; + if(end >= this.buffer.length) { + end = this.buffer.length; + } + this.i = end; + } + + var out = this.buffer.slice(start, end); + var enc = options.encoding; + if(enc == 'utf8' || enc == 'ucs2' || enc == 'binary') { + out = out.toString(enc); + } else { + out = Iconv.decode(out,enc); + } + return out; + }, + int: function(bytes) { + var r = 0; + if(this.remaining() >= bytes) { + if(this.query.byteorder == 'be') { + if(bytes == 1) r = this.buffer.readInt8(this.i); + else if(bytes == 2) r = this.buffer.readInt16BE(this.i); + else if(bytes == 4) r = this.buffer.readInt32BE(this.i); + } else { + if(bytes == 1) r = this.buffer.readInt8(this.i); + else if(bytes == 2) r = this.buffer.readInt16LE(this.i); + else if(bytes == 4) r = this.buffer.readInt32LE(this.i); + } + } + this.i += bytes; + return r; + }, + uint: function(bytes) { + var r = 0; + if(this.remaining() >= bytes) { + if(this.query.byteorder == 'be') { + if(bytes == 1) r = this.buffer.readUInt8(this.i); + else if(bytes == 2) r = this.buffer.readUInt16BE(this.i); + else if(bytes == 4) r = this.buffer.readUInt32BE(this.i); + else if(bytes == 8) r = readUInt64BE(this.buffer,this.i).toString(); + } else { + if(bytes == 1) r = this.buffer.readUInt8(this.i); + else if(bytes == 2) r = this.buffer.readUInt16LE(this.i); + else if(bytes == 4) r = this.buffer.readUInt32LE(this.i); + else if(bytes == 8) r = readUInt64LE(this.buffer,this.i).toString(); + } + } + this.i += bytes; + return r; + }, + float: function() { + var r = 0; + if(this.remaining() >= 4) { + if(this.query.byteorder == 'be') r = this.buffer.readFloatBE(this.i); + else r = this.buffer.readFloatLE(this.i); + } + this.i += 4; + return r; + }, + remaining: function() { + return this.buffer.length-this.i; + }, + rest: function() { + return this.buffer.slice(this.i); + }, + done: function() { + return this.i >= this.buffer.length; + } +}; + +module.exports = Reader; diff --git a/lib/typeresolver.js b/lib/typeresolver.js index f3f3c08..aebaacf 100644 --- a/lib/typeresolver.js +++ b/lib/typeresolver.js @@ -1,91 +1,91 @@ -var Path = require('path'), - fs = require('fs'); - -var protocolDir = Path.normalize(__dirname+'/../protocols'); -var gamesFile = Path.normalize(__dirname+'/../games.txt'); - -function parseList(str) { - if(!str) return {}; - var split = str.split(','); - var out = {}; - split.forEach(function(one) { - var equals = one.indexOf('='); - var key = equals == -1 ? one : one.substr(0,equals); - var value = equals == -1 ? '' : one.substr(equals+1); - - if(value === 'true' || value === '') value = true; - else if(value === 'false') value = false; - else if(!isNaN(value)) value = parseInt(value); - - out[key] = value; - }); - return out; -} -function readGames() { - var lines = fs.readFileSync(gamesFile,'utf8').split('\n'); - var games = {}; - - lines.forEach(function(line) { - // strip comments - var comment = line.indexOf('#'); - if(comment != -1) line = line.substr(0,comment); - line = line.trim(); - if(!line) return; - - var split = line.split('|'); - - games[split[0].trim()] = { - pretty: split[1].trim(), - protocol: split[2].trim(), - options: parseList(split[3]), - params: parseList(split[4]) - }; - }); - return games; -} -var games = readGames(); - -function createProtocolInstance(type) { - type = Path.basename(type); - - var path = protocolDir+'/'+type; - if(!fs.existsSync(path+'.js')) throw Error('Protocol definition file missing: '+type); - var protocol = require(path); - - return new protocol(); -} - -module.exports = { - lookup: function(type) { - if(!type) throw Error('No game specified'); - - if(type.substr(0,9) == 'protocol-') { - return createProtocolInstance(type.substr(9)); - } - - var game = games[type]; - if(!game) throw Error('Invalid game: '+type); - - var query = createProtocolInstance(game.protocol); - query.pretty = game.pretty; - for(var key in game.options) - query.options[key] = game.options[key]; - for(var key in game.params) - query[key] = game.params[key]; - - return query; - }, - printReadme: function() { - var out = ''; - for(var key in games) { - var game = games[key]; - out += "* "+game.pretty+" ("+key+")"; - if(game.options.port_query_offset || game.options.port_query) - out += " [[Separate Query Port](#separate-query-port)]"; - if(game.params.doc_notes) - out += " [[Additional Notes](#"+game.params.doc_notes+")]" - out += "\n"; - } - return out; - } -}; +var Path = require('path'), + fs = require('fs'); + +var protocolDir = Path.normalize(__dirname+'/../protocols'); +var gamesFile = Path.normalize(__dirname+'/../games.txt'); + +function parseList(str) { + if(!str) return {}; + var split = str.split(','); + var out = {}; + split.forEach(function(one) { + var equals = one.indexOf('='); + var key = equals == -1 ? one : one.substr(0,equals); + var value = equals == -1 ? '' : one.substr(equals+1); + + if(value === 'true' || value === '') value = true; + else if(value === 'false') value = false; + else if(!isNaN(value)) value = parseInt(value); + + out[key] = value; + }); + return out; +} +function readGames() { + var lines = fs.readFileSync(gamesFile,'utf8').split('\n'); + var games = {}; + + lines.forEach(function(line) { + // strip comments + var comment = line.indexOf('#'); + if(comment != -1) line = line.substr(0,comment); + line = line.trim(); + if(!line) return; + + var split = line.split('|'); + + games[split[0].trim()] = { + pretty: split[1].trim(), + protocol: split[2].trim(), + options: parseList(split[3]), + params: parseList(split[4]) + }; + }); + return games; +} +var games = readGames(); + +function createProtocolInstance(type) { + type = Path.basename(type); + + var path = protocolDir+'/'+type; + if(!fs.existsSync(path+'.js')) throw Error('Protocol definition file missing: '+type); + var protocol = require(path); + + return new protocol(); +} + +module.exports = { + lookup: function(type) { + if(!type) throw Error('No game specified'); + + if(type.substr(0,9) == 'protocol-') { + return createProtocolInstance(type.substr(9)); + } + + var game = games[type]; + if(!game) throw Error('Invalid game: '+type); + + var query = createProtocolInstance(game.protocol); + query.pretty = game.pretty; + for(var key in game.options) + query.options[key] = game.options[key]; + for(var key in game.params) + query[key] = game.params[key]; + + return query; + }, + printReadme: function() { + var out = ''; + for(var key in games) { + var game = games[key]; + out += "* "+game.pretty+" ("+key+")"; + if(game.options.port_query_offset || game.options.port_query) + out += " [[Separate Query Port](#separate-query-port)]"; + if(game.params.doc_notes) + out += " [[Additional Notes](#"+game.params.doc_notes+")]" + out += "\n"; + } + return out; + } +}; diff --git a/package.json b/package.json index 9e0ce60..e1e6df1 100644 --- a/package.json +++ b/package.json @@ -1,41 +1,41 @@ -{ - "name": "gamedig", - "description": "Query for the status of any game server in Node.JS", - "tags": [ - "srcds", - "query", - "game", - "utility", - "util", - "server" - ], - "main": "lib/index.js", - "author": "Michael Morrison", - "version": "0.2.10", - "repository" : { - "type" : "git", - "url" : "https://github.com/sonicsnes/node-gamedig.git" - }, - "bugs" : { - "url" : "https://github.com/sonicsnes/node-gamedig/issues" - }, - "licenses" : [ - { - "type" : "MIT", - "url" : "https://raw.github.com/sonicsnes/node-gamedig/master/LICENSE" - } - ], - "dependencies": { - "iconv-lite": "~0.2.11", - "long": "~1.1.2", - "async": "~0.2.10", - "compressjs": "~1.0.1", - "gbxremote": "~0.1.4", - "request": "~2.33.0", - "optimist": "~0.6.0", - "varint": "~1.0.0" - }, - "bin": { - "gamedig": "bin/gamedig.js" - } -} +{ + "name": "gamedig", + "description": "Query for the status of any game server in Node.JS", + "tags": [ + "srcds", + "query", + "game", + "utility", + "util", + "server" + ], + "main": "lib/index.js", + "author": "Michael Morrison", + "version": "0.2.10", + "repository" : { + "type" : "git", + "url" : "https://github.com/sonicsnes/node-gamedig.git" + }, + "bugs" : { + "url" : "https://github.com/sonicsnes/node-gamedig/issues" + }, + "licenses" : [ + { + "type" : "MIT", + "url" : "https://raw.github.com/sonicsnes/node-gamedig/master/LICENSE" + } + ], + "dependencies": { + "iconv-lite": "~0.2.11", + "long": "~1.1.2", + "async": "~0.2.10", + "compressjs": "~1.0.1", + "gbxremote": "~0.1.4", + "request": "~2.33.0", + "optimist": "~0.6.0", + "varint": "~1.0.0" + }, + "bin": { + "gamedig": "bin/gamedig.js" + } +} diff --git a/protocols/americasarmy.js b/protocols/americasarmy.js index e8efa32..bd86bf5 100644 --- a/protocols/americasarmy.js +++ b/protocols/americasarmy.js @@ -1,20 +1,20 @@ -module.exports = require('./gamespy2').extend({ - finalizeState: function(state) { - this._super(state); - state.name = this.stripColor(state.name); - state.map = this.stripColor(state.map); - for(var i in state.raw) { - if(!(typeof state.raw[i] == 'string')) continue; - state.raw[i] = this.stripColor(state.raw[i]); - } - for(var i = 0; i < state.players.length; i++) { - var player = state.players[i]; - if(!('name' in player)) continue; - player.name = this.stripColor(player.name); - } - }, - stripColor: function(str) { - // uses unreal 2 color codes - return str.replace(/\x1b...|[\x00-\x1a]/g,''); - } -}); +module.exports = require('./gamespy2').extend({ + finalizeState: function(state) { + this._super(state); + state.name = this.stripColor(state.name); + state.map = this.stripColor(state.map); + for(var i in state.raw) { + if(!(typeof state.raw[i] == 'string')) continue; + state.raw[i] = this.stripColor(state.raw[i]); + } + for(var i = 0; i < state.players.length; i++) { + var player = state.players[i]; + if(!('name' in player)) continue; + player.name = this.stripColor(player.name); + } + }, + stripColor: function(str) { + // uses unreal 2 color codes + return str.replace(/\x1b...|[\x00-\x1a]/g,''); + } +}); diff --git a/protocols/armagetron.js b/protocols/armagetron.js index 906fad9..7f13ef4 100644 --- a/protocols/armagetron.js +++ b/protocols/armagetron.js @@ -1,64 +1,64 @@ -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.encoding = 'latin1'; - this.byteorder = 'be'; - }, - run: function(state) { - var self = this; - - var b = new Buffer([0,0x35,0,0,0,0,0,0x11]); - - this.udpSend(b,function(buffer) { - var reader = self.reader(buffer); - - reader.skip(6); - - state.raw.port = self.readUInt(reader); - state.raw.hostname = self.readString(reader,buffer); - state.name = self.stripColorCodes(self.readString(reader,buffer)); - state.raw.numplayers = self.readUInt(reader); - state.raw.versionmin = self.readUInt(reader); - state.raw.versionmax = self.readUInt(reader); - state.raw.version = self.readString(reader,buffer); - state.maxplayers = self.readUInt(reader); - - var players = self.readString(reader,buffer); - var list = players.split('\n'); - for(var i = 0; i < list.length; i++) { - if(!list[i]) continue; - state.players.push({ - name:self.stripColorCodes(list[i]) - }); - } - - state.raw.options = self.stripColorCodes(self.readString(reader,buffer)); - state.raw.uri = self.readString(reader,buffer); - state.raw.globalids = self.readString(reader,buffer); - self.finish(state); - return true; - }); - }, - readUInt: function(reader) { - var a = reader.uint(2); - var b = reader.uint(2); - return (b<<16) + a; - }, - readString: function(reader,b) { - var len = reader.uint(2); - if(!len) return ''; - - var out = ''; - for(var i = 0; i < len; i+=2) { - var hi = reader.uint(1); - var lo = reader.uint(1); - if(i+1[^]*([^]*)<\/td>[^]*([^]*)<\/td>[^]*([^]*)<\/td>[^]*([^]*)<\/td>/g; - while(pm = pre.exec(table)) { - if(pm[2] == 'Ping') continue; - state.players.push({ - name: pm[1], - ping: pm[2], - team: pm[3], - score: pm[4] - }); - } - } - /* - var m = this.options.address.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/); - if(m) { - var o1 = parseInt(m[1]); - var o2 = parseInt(m[2]); - var o3 = parseInt(m[3]); - var o4 = parseInt(m[4]); - var addr = o1+(o2<<8)+(o3<<16)+(o4<<24); - state.raw.url = 'aos://'+addr; - } - */ - self.finish(state); - }); - } -}); +var request = require('request'); + +module.exports = require('./core').extend({ + run: function(state) { + var self = this; + request({ + uri: 'http://'+this.options.address+':'+this.options.port_query+'/', + timeout: 3000, + }, function(e,r,body) { + if(e) return self.fatal('HTTP error'); + + var m = body.match(/status server for (.*?)\r|\n/); + if(m) state.name = m[1]; + + var m = body.match(/Current uptime: (\d+)/); + if(m) state.raw.uptime = m[1]; + + var m = body.match(/currently running (.*?) by /); + if(m) state.map = m[1]; + + var m = body.match(/Current players: (\d+)\/(\d+)/); + if(m) { + state.raw.numplayers = m[1]; + state.maxplayers = m[2]; + } + + var m = body.match(/class="playerlist"([^]+?)\/table/); + if(m) { + var table = m[1]; + var pre = /[^]*([^]*)<\/td>[^]*([^]*)<\/td>[^]*([^]*)<\/td>[^]*([^]*)<\/td>/g; + while(pm = pre.exec(table)) { + if(pm[2] == 'Ping') continue; + state.players.push({ + name: pm[1], + ping: pm[2], + team: pm[3], + score: pm[4] + }); + } + } + /* + var m = this.options.address.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/); + if(m) { + var o1 = parseInt(m[1]); + var o2 = parseInt(m[2]); + var o3 = parseInt(m[3]); + var o4 = parseInt(m[4]); + var addr = o1+(o2<<8)+(o3<<16)+(o4<<24); + state.raw.url = 'aos://'+addr; + } + */ + self.finish(state); + }); + } +}); diff --git a/protocols/core.js b/protocols/core.js index 26d28cb..49cbab7 100644 --- a/protocols/core.js +++ b/protocols/core.js @@ -1,312 +1,312 @@ -var EventEmitter = require('events').EventEmitter, - dns = require('dns'), - net = require('net'), - async = require('async'), - Class = require('../lib/Class'), - Reader = require('../lib/reader'); - -module.exports = Class.extend(EventEmitter,{ - init: function() { - this._super(); - this.options = { - tcpTimeout: 1000, - udpTimeout: 1000 - }; - this.maxAttempts = 1; - this.attempt = 1; - this.finished = false; - this.encoding = 'utf8'; - this.byteorder = 'le'; - this.delimiter = '\0'; - - var self = this; - this.globalTimeoutTimer = setTimeout(function() { - self.fatal('timeout'); - },10000); - }, - - fatal: function(err,noretry) { - if(!noretry && this.attempt < this.maxAttempts) { - this.attempt++; - this.start(); - return; - } - - this.done({error: err.toString()}); - }, - initState: function() { - return { - name: '', - map: '', - password: false, - - raw: {}, - - maxplayers: 0, - players: [], - bots: [] - }; - }, - finalizeState: function(state) {}, - - finish: function(state) { - this.finalizeState(state); - this.done(state); - }, - - done: function(state) { - if(this.finished) return; - clearTimeout(this.globalTimeoutTimer); - - if(this.options.notes) - state.notes = this.options.notes; - - state.query = {}; - if('host' in this.options) state.query.host = this.options.host; - if('address' in this.options) state.query.address = this.options.address; - if('port' in this.options) state.query.port = this.options.port; - if('port_query' in this.options) state.query.port_query = this.options.port_query; - state.query.type = this.type; - if('pretty' in this) state.query.pretty = this.pretty; - - this.reset(); - this.finished = true; - this.emit('finished',state); - if(this.options.callback) this.options.callback(state); - }, - - reset: function() { - if(this.timers) { - this.timers.forEach(function(timer) { - clearTimeout(timer); - }); - } - this.timers = []; - - if(this.tcpSocket) { - this.tcpSocket.destroy(); - delete this.tcpSocket; - } - - this.udpTimeoutTimer = false; - this.udpCallback = false; - }, - start: function() { - var self = this; - var options = self.options; - this.reset(); - - async.series([ - function(c) { - // resolve host names - if(!('host' in options)) return c(); - if(options.host.match(/\d+\.\d+\.\d+\.\d+/)) { - options.address = options.host; - c(); - } else { - self.parseDns(options.host,c); - } - }, - function(c) { - // calculate query port if needed - if(!('port_query' in options) && 'port' in options) { - var offset = options.port_query_offset || 0; - options.port_query = options.port + offset; - } - c(); - }, - function(c) { - // run - self.run(self.initState()); - } - - ]); - }, - parseDns: function(host,c) { - var self = this; - - function resolveStandard(host,c) { - dns.lookup(host, function(err,address,family) { - if(err) return self.fatal(err); - self.options.address = address; - c(); - }); - } - function resolveSrv(srv,host,c) { - dns.resolve(srv+'.'+host, 'SRV', function(err,addresses) { - if(err) return resolveStandard(host,c); - if(addresses.length >= 1) { - var line = addresses[0]; - self.options.port = line.port; - var srvhost = line.name; - - if(srvhost.match(/\d+\.\d+\.\d+\.\d+/)) { - self.options.address = srvhost; - c(); - } else { - // resolve yet again - resolveStandard(srvhost,c); - } - return; - } - return resolveStandard(host,c); - }); - } - - if(this.srvRecord) resolveSrv(this.srvRecord,host,c); - else resolveStandard(host,c); - }, - - // utils - reader: function(buffer) { - return new Reader(this,buffer); - }, - translate: function(obj,trans) { - for(var from in trans) { - var to = trans[from]; - if(from in obj) { - if(to) obj[to] = obj[from]; - delete obj[from]; - } - } - }, - setTimeout: function(c,t) { - if(this.finished) return 0; - var id = setTimeout(c,t); - this.timers.push(id); - return id; - }, - - - - trueTest: function(str) { - if(typeof str == 'boolean') return str; - if(typeof str == 'number') return str != 0; - if(typeof str == 'string') { - if(str.toLowerCase() == 'true') return true; - if(str == 'yes') return true; - if(str == '1') return true; - } - return false; - }, - debugBuffer: function(buffer) { - var out = ''; - var out2 = ''; - for(var i = 0; i < buffer.length; i++) { - var sliced = buffer.slice(i,i+1); - out += sliced.toString('hex')+' '; - var chr = sliced.toString(); - if(chr < ' ' || chr > '~') chr = ' '; - out2 += chr+' '; - if(out.length > 60) { - console.log(out); - console.log(out2); - out = out2 = ''; - } - } - console.log(out); - console.log(out2); - }, - - - - - _tcpConnect: function(c) { - var self = this; - if(this.tcpSocket) return c(this.tcpSocket); - - var connected = false; - var received = new Buffer(0); - var address = this.options.address; - var port = this.options.port_query; - - var socket = this.tcpSocket = net.connect(port,address,function() { - if(self.debug) console.log(address+':'+port+" TCPCONNECTED"); - connected = true; - c(socket); - }); - socket.setTimeout(10000); - socket.setNoDelay(true); - if(this.debug) console.log(address+':'+port+" TCPCONNECT"); - - var writeHook = socket.write; - socket.write = function(data) { - if(self.debug) console.log(address+':'+port+" TCP--> "+data.toString('hex')); - writeHook.apply(this,arguments); - } - - socket.on('error', function() {}); - socket.on('close', function() { - if(!self.tcpCallback) return; - if(connected) return self.fatal('Socket closed while waiting on TCP'); - else return self.fatal('TCP Connection Refused'); - }); - socket.on('data', function(data) { - if(!self.tcpCallback) return; - if(self.debug) console.log(address+':'+port+" <--TCP "+data.toString('hex')); - received = Buffer.concat([received,data]); - if(self.tcpCallback(received)) { - clearTimeout(self.tcpTimeoutTimer); - self.tcpCallback = false; - received = new Buffer(0); - } - }); - }, - tcpSend: function(buffer,ondata) { - var self = this; - process.nextTick(function() { - if(self.tcpCallback) return self.fatal('Attempted to send TCP packet while still waiting on a managed response'); - self._tcpConnect(function(socket) { - socket.write(buffer); - }); - if(!ondata) return; - - self.tcpTimeoutTimer = self.setTimeout(function() { - self.tcpCallback = false; - self.fatal('TCP Watchdog Timeout'); - },self.options.tcpTimeout); - self.tcpCallback = ondata; - }); - }, - - - - udpSend: function(buffer,onpacket,ontimeout) { - var self = this; - process.nextTick(function() { - if(self.udpCallback) return self.fatal('Attempted to send UDP packet while still waiting on a managed response'); - self._udpSendNow(buffer); - if(!onpacket) return; - - self.udpTimeoutTimer = self.setTimeout(function() { - self.udpCallback = false; - var timeout = false; - if(!ontimeout || ontimeout() !== true) timeout = true; - if(timeout) self.fatal('UDP Watchdog Timeout'); - },self.options.udpTimeout); - self.udpCallback = onpacket; - }); - }, - _udpSendNow: function(buffer) { - if(!('port_query' in this.options)) return this.fatal('Attempted to send without setting a port'); - if(!('address' in this.options)) return this.fatal('Attempted to send without setting an address'); - - if(typeof buffer == 'string') buffer = new Buffer(buffer,'binary'); - - if(this.debug) console.log(this.options.address+':'+this.options.port_query+" UDP--> "+buffer.toString('hex')); - this.udpSocket.send(buffer,0,buffer.length,this.options.port_query,this.options.address); - }, - _udpResponse: function(buffer) { - if(this.udpCallback) { - var result = this.udpCallback(buffer); - if(result === true) { - // we're done with this udp session - clearTimeout(this.udpTimeoutTimer); - this.udpCallback = false; - } - } else { - this.udpResponse(buffer); - } - }, - udpResponse: function() {} -}); +var EventEmitter = require('events').EventEmitter, + dns = require('dns'), + net = require('net'), + async = require('async'), + Class = require('../lib/Class'), + Reader = require('../lib/reader'); + +module.exports = Class.extend(EventEmitter,{ + init: function() { + this._super(); + this.options = { + tcpTimeout: 1000, + udpTimeout: 1000 + }; + this.maxAttempts = 1; + this.attempt = 1; + this.finished = false; + this.encoding = 'utf8'; + this.byteorder = 'le'; + this.delimiter = '\0'; + + var self = this; + this.globalTimeoutTimer = setTimeout(function() { + self.fatal('timeout'); + },10000); + }, + + fatal: function(err,noretry) { + if(!noretry && this.attempt < this.maxAttempts) { + this.attempt++; + this.start(); + return; + } + + this.done({error: err.toString()}); + }, + initState: function() { + return { + name: '', + map: '', + password: false, + + raw: {}, + + maxplayers: 0, + players: [], + bots: [] + }; + }, + finalizeState: function(state) {}, + + finish: function(state) { + this.finalizeState(state); + this.done(state); + }, + + done: function(state) { + if(this.finished) return; + clearTimeout(this.globalTimeoutTimer); + + if(this.options.notes) + state.notes = this.options.notes; + + state.query = {}; + if('host' in this.options) state.query.host = this.options.host; + if('address' in this.options) state.query.address = this.options.address; + if('port' in this.options) state.query.port = this.options.port; + if('port_query' in this.options) state.query.port_query = this.options.port_query; + state.query.type = this.type; + if('pretty' in this) state.query.pretty = this.pretty; + + this.reset(); + this.finished = true; + this.emit('finished',state); + if(this.options.callback) this.options.callback(state); + }, + + reset: function() { + if(this.timers) { + this.timers.forEach(function(timer) { + clearTimeout(timer); + }); + } + this.timers = []; + + if(this.tcpSocket) { + this.tcpSocket.destroy(); + delete this.tcpSocket; + } + + this.udpTimeoutTimer = false; + this.udpCallback = false; + }, + start: function() { + var self = this; + var options = self.options; + this.reset(); + + async.series([ + function(c) { + // resolve host names + if(!('host' in options)) return c(); + if(options.host.match(/\d+\.\d+\.\d+\.\d+/)) { + options.address = options.host; + c(); + } else { + self.parseDns(options.host,c); + } + }, + function(c) { + // calculate query port if needed + if(!('port_query' in options) && 'port' in options) { + var offset = options.port_query_offset || 0; + options.port_query = options.port + offset; + } + c(); + }, + function(c) { + // run + self.run(self.initState()); + } + + ]); + }, + parseDns: function(host,c) { + var self = this; + + function resolveStandard(host,c) { + dns.lookup(host, function(err,address,family) { + if(err) return self.fatal(err); + self.options.address = address; + c(); + }); + } + function resolveSrv(srv,host,c) { + dns.resolve(srv+'.'+host, 'SRV', function(err,addresses) { + if(err) return resolveStandard(host,c); + if(addresses.length >= 1) { + var line = addresses[0]; + self.options.port = line.port; + var srvhost = line.name; + + if(srvhost.match(/\d+\.\d+\.\d+\.\d+/)) { + self.options.address = srvhost; + c(); + } else { + // resolve yet again + resolveStandard(srvhost,c); + } + return; + } + return resolveStandard(host,c); + }); + } + + if(this.srvRecord) resolveSrv(this.srvRecord,host,c); + else resolveStandard(host,c); + }, + + // utils + reader: function(buffer) { + return new Reader(this,buffer); + }, + translate: function(obj,trans) { + for(var from in trans) { + var to = trans[from]; + if(from in obj) { + if(to) obj[to] = obj[from]; + delete obj[from]; + } + } + }, + setTimeout: function(c,t) { + if(this.finished) return 0; + var id = setTimeout(c,t); + this.timers.push(id); + return id; + }, + + + + trueTest: function(str) { + if(typeof str == 'boolean') return str; + if(typeof str == 'number') return str != 0; + if(typeof str == 'string') { + if(str.toLowerCase() == 'true') return true; + if(str == 'yes') return true; + if(str == '1') return true; + } + return false; + }, + debugBuffer: function(buffer) { + var out = ''; + var out2 = ''; + for(var i = 0; i < buffer.length; i++) { + var sliced = buffer.slice(i,i+1); + out += sliced.toString('hex')+' '; + var chr = sliced.toString(); + if(chr < ' ' || chr > '~') chr = ' '; + out2 += chr+' '; + if(out.length > 60) { + console.log(out); + console.log(out2); + out = out2 = ''; + } + } + console.log(out); + console.log(out2); + }, + + + + + _tcpConnect: function(c) { + var self = this; + if(this.tcpSocket) return c(this.tcpSocket); + + var connected = false; + var received = new Buffer(0); + var address = this.options.address; + var port = this.options.port_query; + + var socket = this.tcpSocket = net.connect(port,address,function() { + if(self.debug) console.log(address+':'+port+" TCPCONNECTED"); + connected = true; + c(socket); + }); + socket.setTimeout(10000); + socket.setNoDelay(true); + if(this.debug) console.log(address+':'+port+" TCPCONNECT"); + + var writeHook = socket.write; + socket.write = function(data) { + if(self.debug) console.log(address+':'+port+" TCP--> "+data.toString('hex')); + writeHook.apply(this,arguments); + } + + socket.on('error', function() {}); + socket.on('close', function() { + if(!self.tcpCallback) return; + if(connected) return self.fatal('Socket closed while waiting on TCP'); + else return self.fatal('TCP Connection Refused'); + }); + socket.on('data', function(data) { + if(!self.tcpCallback) return; + if(self.debug) console.log(address+':'+port+" <--TCP "+data.toString('hex')); + received = Buffer.concat([received,data]); + if(self.tcpCallback(received)) { + clearTimeout(self.tcpTimeoutTimer); + self.tcpCallback = false; + received = new Buffer(0); + } + }); + }, + tcpSend: function(buffer,ondata) { + var self = this; + process.nextTick(function() { + if(self.tcpCallback) return self.fatal('Attempted to send TCP packet while still waiting on a managed response'); + self._tcpConnect(function(socket) { + socket.write(buffer); + }); + if(!ondata) return; + + self.tcpTimeoutTimer = self.setTimeout(function() { + self.tcpCallback = false; + self.fatal('TCP Watchdog Timeout'); + },self.options.tcpTimeout); + self.tcpCallback = ondata; + }); + }, + + + + udpSend: function(buffer,onpacket,ontimeout) { + var self = this; + process.nextTick(function() { + if(self.udpCallback) return self.fatal('Attempted to send UDP packet while still waiting on a managed response'); + self._udpSendNow(buffer); + if(!onpacket) return; + + self.udpTimeoutTimer = self.setTimeout(function() { + self.udpCallback = false; + var timeout = false; + if(!ontimeout || ontimeout() !== true) timeout = true; + if(timeout) self.fatal('UDP Watchdog Timeout'); + },self.options.udpTimeout); + self.udpCallback = onpacket; + }); + }, + _udpSendNow: function(buffer) { + if(!('port_query' in this.options)) return this.fatal('Attempted to send without setting a port'); + if(!('address' in this.options)) return this.fatal('Attempted to send without setting an address'); + + if(typeof buffer == 'string') buffer = new Buffer(buffer,'binary'); + + if(this.debug) console.log(this.options.address+':'+this.options.port_query+" UDP--> "+buffer.toString('hex')); + this.udpSocket.send(buffer,0,buffer.length,this.options.port_query,this.options.address); + }, + _udpResponse: function(buffer) { + if(this.udpCallback) { + var result = this.udpCallback(buffer); + if(result === true) { + // we're done with this udp session + clearTimeout(this.udpTimeoutTimer); + this.udpCallback = false; + } + } else { + this.udpResponse(buffer); + } + }, + udpResponse: function() {} +}); diff --git a/protocols/doom3.js b/protocols/doom3.js index ab8cbdb..9379fba 100644 --- a/protocols/doom3.js +++ b/protocols/doom3.js @@ -1,95 +1,95 @@ -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.pretty = 'Doom 3'; - this.encoding = 'latin1'; - this.isEtqw = false; - this.hasSpaceBeforeClanTag = false; - this.hasClanTag = false; - this.hasTypeFlag = false; - }, - run: function(state) { - var self = this; - - this.udpSend('\xff\xffgetInfo\x00PiNGPoNG\x00',function(buffer) { - var reader = self.reader(buffer); - - var header = reader.uint(2); - if(header != 0xffff) return; - var header2 = reader.string(); - if(header2 != 'infoResponse') return; - - var tailSize = 5; - if(self.isEtqw) { - var taskId = reader.uint(4); - } - - var challenge = reader.uint(4); - var protoVersion = reader.uint(4); - state.raw.protocolVersion = (protoVersion>>16)+'.'+(protoVersion&0xffff); - - if(self.isEtqw) { - var size = reader.uint(4); - } - - while(!reader.done()) { - var key = reader.string(); - var value = self.stripColors(reader.string()); - if(key == 'si_map') { - value = value.replace('maps/',''); - value = value.replace('.entities',''); - } - if(!key) break; - state.raw[key] = value; - } - - var i = 0; - while(!reader.done()) { - i++; - var player = {}; - player.id = reader.uint(1); - if(player.id == 32) break; - player.ping = reader.uint(2); - if(!self.isEtqw) player.rate = reader.uint(4); - player.name = self.stripColors(reader.string()); - if(self.hasClanTag) { - if(self.hasSpaceBeforeClanTag) reader.uint(1); - player.clantag = self.stripColors(reader.string()); - } - if(self.hasTypeFlag) player.typeflag = reader.uint(1); - - if(!player.ping || player.typeflag) - state.bots.push(player); - else - state.players.push(player); - } - - state.raw.osmask = reader.uint(4); - if(self.isEtqw) { - state.raw.ranked = reader.uint(1); - state.raw.timeleft = reader.uint(4); - state.raw.gamestate = reader.uint(1); - state.raw.servertype = reader.uint(1); - // 0 = regular, 1 = tv - if(state.raw.servertype == 0) { - state.raw.interestedClients = reader.uint(1); - } else if(state.raw.servertype == 1) { - state.raw.connectedClients = reader.uint(4); - state.raw.maxClients = reader.uint(4); - } - } - - if(state.raw.si_name) state.name = state.raw.si_name; - if(state.raw.si_map) state.map = state.raw.si_map; - if(state.raw.si_maxplayers) state.maxplayers = parseInt(state.raw.si_maxplayers); - if(state.raw.si_usepass == '1') state.password = true; - - self.finish(state); - return true; - }); - }, - stripColors: function(str) { - // uses quake 3 color codes - return str.replace(/\^(X.{6}|.)/g,''); - } -}); +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.pretty = 'Doom 3'; + this.encoding = 'latin1'; + this.isEtqw = false; + this.hasSpaceBeforeClanTag = false; + this.hasClanTag = false; + this.hasTypeFlag = false; + }, + run: function(state) { + var self = this; + + this.udpSend('\xff\xffgetInfo\x00PiNGPoNG\x00',function(buffer) { + var reader = self.reader(buffer); + + var header = reader.uint(2); + if(header != 0xffff) return; + var header2 = reader.string(); + if(header2 != 'infoResponse') return; + + var tailSize = 5; + if(self.isEtqw) { + var taskId = reader.uint(4); + } + + var challenge = reader.uint(4); + var protoVersion = reader.uint(4); + state.raw.protocolVersion = (protoVersion>>16)+'.'+(protoVersion&0xffff); + + if(self.isEtqw) { + var size = reader.uint(4); + } + + while(!reader.done()) { + var key = reader.string(); + var value = self.stripColors(reader.string()); + if(key == 'si_map') { + value = value.replace('maps/',''); + value = value.replace('.entities',''); + } + if(!key) break; + state.raw[key] = value; + } + + var i = 0; + while(!reader.done()) { + i++; + var player = {}; + player.id = reader.uint(1); + if(player.id == 32) break; + player.ping = reader.uint(2); + if(!self.isEtqw) player.rate = reader.uint(4); + player.name = self.stripColors(reader.string()); + if(self.hasClanTag) { + if(self.hasSpaceBeforeClanTag) reader.uint(1); + player.clantag = self.stripColors(reader.string()); + } + if(self.hasTypeFlag) player.typeflag = reader.uint(1); + + if(!player.ping || player.typeflag) + state.bots.push(player); + else + state.players.push(player); + } + + state.raw.osmask = reader.uint(4); + if(self.isEtqw) { + state.raw.ranked = reader.uint(1); + state.raw.timeleft = reader.uint(4); + state.raw.gamestate = reader.uint(1); + state.raw.servertype = reader.uint(1); + // 0 = regular, 1 = tv + if(state.raw.servertype == 0) { + state.raw.interestedClients = reader.uint(1); + } else if(state.raw.servertype == 1) { + state.raw.connectedClients = reader.uint(4); + state.raw.maxClients = reader.uint(4); + } + } + + if(state.raw.si_name) state.name = state.raw.si_name; + if(state.raw.si_map) state.map = state.raw.si_map; + if(state.raw.si_maxplayers) state.maxplayers = parseInt(state.raw.si_maxplayers); + if(state.raw.si_usepass == '1') state.password = true; + + self.finish(state); + return true; + }); + }, + stripColors: function(str) { + // uses quake 3 color codes + return str.replace(/\^(X.{6}|.)/g,''); + } +}); diff --git a/protocols/ffow.js b/protocols/ffow.js index 589f222..e346399 100644 --- a/protocols/ffow.js +++ b/protocols/ffow.js @@ -1,33 +1,33 @@ -module.exports = require('./valve').extend({ - init: function() { - this._super(); - this.byteorder = 'be'; - this.legacyChallenge = true; - }, - queryInfo: function(state,c) { - var self = this; - self.sendPacket(0x46,false,'LSQ',0x49,function(b) { - var reader = self.reader(b); - - state.raw.protocol = reader.uint(1); - state.name = reader.string(); - state.map = reader.string(); - state.raw.mod = reader.string(); - state.raw.gamemode = reader.string(); - state.raw.description = reader.string(); - state.raw.version = reader.string(); - state.raw.port = reader.uint(2); - state.raw.numplayers = reader.uint(1); - state.maxplayers = reader.uint(1); - state.raw.listentype = String.fromCharCode(reader.uint(1)); - state.raw.environment = String.fromCharCode(reader.uint(1)); - state.password = !!reader.uint(1); - state.raw.secure = reader.uint(1); - state.raw.averagefps = reader.uint(1); - state.raw.round = reader.uint(1); - state.raw.maxrounds = reader.uint(1); - state.raw.timeleft = reader.uint(2); - c(); - }); - } -}); +module.exports = require('./valve').extend({ + init: function() { + this._super(); + this.byteorder = 'be'; + this.legacyChallenge = true; + }, + queryInfo: function(state,c) { + var self = this; + self.sendPacket(0x46,false,'LSQ',0x49,function(b) { + var reader = self.reader(b); + + state.raw.protocol = reader.uint(1); + state.name = reader.string(); + state.map = reader.string(); + state.raw.mod = reader.string(); + state.raw.gamemode = reader.string(); + state.raw.description = reader.string(); + state.raw.version = reader.string(); + state.raw.port = reader.uint(2); + state.raw.numplayers = reader.uint(1); + state.maxplayers = reader.uint(1); + state.raw.listentype = String.fromCharCode(reader.uint(1)); + state.raw.environment = String.fromCharCode(reader.uint(1)); + state.password = !!reader.uint(1); + state.raw.secure = reader.uint(1); + state.raw.averagefps = reader.uint(1); + state.raw.round = reader.uint(1); + state.raw.maxrounds = reader.uint(1); + state.raw.timeleft = reader.uint(2); + c(); + }); + } +}); diff --git a/protocols/gamespy1.js b/protocols/gamespy1.js index 5e61dff..f2379c4 100644 --- a/protocols/gamespy1.js +++ b/protocols/gamespy1.js @@ -1,85 +1,85 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.sessionId = 1; - this.encoding = 'latin1'; - this.byteorder = 'be'; - }, - run: function(state) { - var self = this; - - async.series([ - function(c) { - self.sendPacket('info', function(data) { - state.raw = data; - if('hostname' in state.raw) state.name = state.raw.hostname; - if('mapname' in state.raw) state.map = state.raw.mapname; - if(self.trueTest(state.raw.password)) state.password = true; - if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); - c(); - }); - }, - function(c) { - self.sendPacket('rules', function(data) { - state.raw.rules = data; - c(); - }); - }, - function(c) { - self.sendPacket('players', function(data) { - var players = {}; - var teams = {}; - for(var ident in data) { - var split = ident.split('_'); - var key = split[0]; - var id = split[1]; - var value = data[ident]; - - if(key == 'teamname') { - teams[id] = value; - } else { - if(!(id in players)) players[id] = {}; - if(key == 'playername') key = 'name'; - else if(key == 'team') value = parseInt(value); - else if(key == 'score' || key == 'ping' || key == 'deaths') value = parseInt(value); - players[id][key] = value; - } - } - - state.raw.teams = teams; - for(var i in players) state.players.push(players[i]); - self.finish(state); - }); - } - ]); - - }, - sendPacket: function(type,callback) { - var self = this; - var queryId = ''; - var output = {}; - this.udpSend('\\'+type+'\\',function(buffer) { - var reader = self.reader(buffer); - var str = reader.string({length:buffer.length}); - var split = str.split('\\'); - split.shift(); - var data = {}; - while(split.length) { - var key = split.shift(); - var value = split.shift() || ''; - data[key] = value; - } - if(!('queryid' in data)) return; - if(queryId && data.queryid != queryId) return; - for(var i in data) output[i] = data[i]; - if('final' in output) { - delete output.final; - delete output.queryid; - callback(output); - return true; - } - }); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.sessionId = 1; + this.encoding = 'latin1'; + this.byteorder = 'be'; + }, + run: function(state) { + var self = this; + + async.series([ + function(c) { + self.sendPacket('info', function(data) { + state.raw = data; + if('hostname' in state.raw) state.name = state.raw.hostname; + if('mapname' in state.raw) state.map = state.raw.mapname; + if(self.trueTest(state.raw.password)) state.password = true; + if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); + c(); + }); + }, + function(c) { + self.sendPacket('rules', function(data) { + state.raw.rules = data; + c(); + }); + }, + function(c) { + self.sendPacket('players', function(data) { + var players = {}; + var teams = {}; + for(var ident in data) { + var split = ident.split('_'); + var key = split[0]; + var id = split[1]; + var value = data[ident]; + + if(key == 'teamname') { + teams[id] = value; + } else { + if(!(id in players)) players[id] = {}; + if(key == 'playername') key = 'name'; + else if(key == 'team') value = parseInt(value); + else if(key == 'score' || key == 'ping' || key == 'deaths') value = parseInt(value); + players[id][key] = value; + } + } + + state.raw.teams = teams; + for(var i in players) state.players.push(players[i]); + self.finish(state); + }); + } + ]); + + }, + sendPacket: function(type,callback) { + var self = this; + var queryId = ''; + var output = {}; + this.udpSend('\\'+type+'\\',function(buffer) { + var reader = self.reader(buffer); + var str = reader.string({length:buffer.length}); + var split = str.split('\\'); + split.shift(); + var data = {}; + while(split.length) { + var key = split.shift(); + var value = split.shift() || ''; + data[key] = value; + } + if(!('queryid' in data)) return; + if(queryId && data.queryid != queryId) return; + for(var i in data) output[i] = data[i]; + if('final' in output) { + delete output.final; + delete output.queryid; + callback(output); + return true; + } + }); + } +}); diff --git a/protocols/gamespy2.js b/protocols/gamespy2.js index 0b21d63..86b4596 100644 --- a/protocols/gamespy2.js +++ b/protocols/gamespy2.js @@ -1,96 +1,96 @@ -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.sessionId = 1; - this.encoding = 'latin1'; - this.byteorder = 'be'; - }, - run: function(state) { - var self = this; - - var request = new Buffer([0xfe,0xfd,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff]); - var packets = []; - this.udpSend(request, - function(buffer) { - if(packets.length && buffer.readUInt8(0) == 0) - buffer = buffer.slice(1); - packets.push(buffer); - }, - function() { - var buffer = Buffer.concat(packets); - var reader = self.reader(buffer); - var header = reader.uint(1); - if(header != 0) return; - var pingId = reader.uint(4); - if(pingId != 1) return; - - while(!reader.done()) { - var key = reader.string(); - var value = reader.string(); - if(!key) break; - state.raw[key] = value; - } - - if('hostname' in state.raw) state.name = state.raw.hostname; - if('mapname' in state.raw) state.map = state.raw.mapname; - if(self.trueTest(state.raw.password)) state.password = true; - if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); - - state.players = self.readFieldData(reader); - state.raw.teams = self.readFieldData(reader); - - self.finish(state); - return true; - } - ); - }, - readFieldData: function(reader) { - var count = reader.uint(1); - // count is unreliable (often it's wrong), so we don't use it. - // read until we hit an empty first field string - - if(this.debug) console.log("Reading fields, starting at: "+reader.rest()); - - var fields = []; - while(!reader.done()) { - var field = reader.string(); - if(!field) break; - if(field.charCodeAt(0) <= 2) field = field.substring(1); - fields.push(field); - if(this.debug) console.log("field:"+field); - } - - var units = []; - outer: while(!reader.done()) { - var unit = {}; - for(var iField = 0; iField < fields.length; iField++) { - var key = fields[iField]; - var value = reader.string(); - if(!value && iField == 0) break outer; - if(this.debug) console.log("value:"+value); - if(key == 'player_') key = 'name'; - else if(key == 'score_') key = 'score'; - else if(key == 'deaths_') key = 'deaths'; - else if(key == 'ping_') key = 'ping'; - else if(key == 'team_') key = 'team'; - else if(key == 'kills_') key = 'kills'; - else if(key == 'team_t') key = 'name'; - else if(key == 'tickets_t') key = 'tickets'; - - if( - key == 'score' || key == 'deaths' - || key == 'ping' || key == 'team' - || key == 'kills' || key == 'tickets' - ) { - if(value === '') continue; - value = parseInt(value); - } - - unit[key] = value; - } - units.push(unit); - } - - return units; - } -}); +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.sessionId = 1; + this.encoding = 'latin1'; + this.byteorder = 'be'; + }, + run: function(state) { + var self = this; + + var request = new Buffer([0xfe,0xfd,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff]); + var packets = []; + this.udpSend(request, + function(buffer) { + if(packets.length && buffer.readUInt8(0) == 0) + buffer = buffer.slice(1); + packets.push(buffer); + }, + function() { + var buffer = Buffer.concat(packets); + var reader = self.reader(buffer); + var header = reader.uint(1); + if(header != 0) return; + var pingId = reader.uint(4); + if(pingId != 1) return; + + while(!reader.done()) { + var key = reader.string(); + var value = reader.string(); + if(!key) break; + state.raw[key] = value; + } + + if('hostname' in state.raw) state.name = state.raw.hostname; + if('mapname' in state.raw) state.map = state.raw.mapname; + if(self.trueTest(state.raw.password)) state.password = true; + if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); + + state.players = self.readFieldData(reader); + state.raw.teams = self.readFieldData(reader); + + self.finish(state); + return true; + } + ); + }, + readFieldData: function(reader) { + var count = reader.uint(1); + // count is unreliable (often it's wrong), so we don't use it. + // read until we hit an empty first field string + + if(this.debug) console.log("Reading fields, starting at: "+reader.rest()); + + var fields = []; + while(!reader.done()) { + var field = reader.string(); + if(!field) break; + if(field.charCodeAt(0) <= 2) field = field.substring(1); + fields.push(field); + if(this.debug) console.log("field:"+field); + } + + var units = []; + outer: while(!reader.done()) { + var unit = {}; + for(var iField = 0; iField < fields.length; iField++) { + var key = fields[iField]; + var value = reader.string(); + if(!value && iField == 0) break outer; + if(this.debug) console.log("value:"+value); + if(key == 'player_') key = 'name'; + else if(key == 'score_') key = 'score'; + else if(key == 'deaths_') key = 'deaths'; + else if(key == 'ping_') key = 'ping'; + else if(key == 'team_') key = 'team'; + else if(key == 'kills_') key = 'kills'; + else if(key == 'team_t') key = 'name'; + else if(key == 'tickets_t') key = 'tickets'; + + if( + key == 'score' || key == 'deaths' + || key == 'ping' || key == 'team' + || key == 'kills' || key == 'tickets' + ) { + if(value === '') continue; + value = parseInt(value); + } + + unit[key] = value; + } + units.push(unit); + } + + return units; + } +}); diff --git a/protocols/gamespy3.js b/protocols/gamespy3.js index 17ea3f1..e2dcaa3 100644 --- a/protocols/gamespy3.js +++ b/protocols/gamespy3.js @@ -1,168 +1,168 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.sessionId = 1; - this.encoding = 'latin1'; - this.byteorder = 'be'; - this.noChallenge = false; - this.useOnlySingleSplit = false; - }, - run: function(state) { - var self = this; - var challenge,packets; - - async.series([ - function(c) { - if(self.noChallenge) return c(); - self.sendPacket(9,false,false,false,function(buffer) { - var reader = self.reader(buffer); - challenge = parseInt(reader.string()); - c(); - }); - }, - function(c) { - self.sendPacket(0,challenge,new Buffer([0xff,0xff,0xff,0x01]),true,function(b) { - packets = b; - c(); - }); - }, - function(c) { - // iterate over the received packets - // the first packet will start off with k/v pairs, followed with data fields - // the following packets will only have data fields - var data = {}; - - for(var iPacket = 0; iPacket < packets.length; iPacket++) { - var packet = packets[iPacket]; - var reader = self.reader(packet); - - if(self.debug) { - console.log("+++"+packet.toString('hex')); - console.log(":::"+packet.toString('ascii')); - } - - if(iPacket == 0) { - while(!reader.done()) { - var key = reader.string(); - if(!key) break; - var value = reader.string(); - - // reread the next line if we hit the weird ut3 bug - if(value == 'p1073741829') value = reader.string(); - - state.raw[key] = value; - } - } - - var firstMode = true; - while(!reader.done()) { - var mode = reader.string(); - if(mode.charCodeAt(0) <= 2) mode = mode.substring(1); - if(!mode) continue; - var offset = 0; - if(iPacket != 0 && firstMode) offset = reader.uint(1); - reader.skip(1); - firstMode = false; - - while(!reader.done()) { - var item = reader.string(); - if(!item) break; - - if( - mode == 'player_' - || mode == 'score_' - || mode == 'ping_' - || mode == 'team_' - || mode == 'deaths_' - || mode == 'pid_' - ) { - if(state.players.length <= offset) - state.players.push({}); - } - if(mode == 'player_') state.players[offset].name = item; - if(mode == 'score_') state.players[offset].score = parseInt(item); - if(mode == 'ping_') state.players[offset].ping = parseInt(item); - if(mode == 'team_') state.players[offset].team = parseInt(item); - if(mode == 'deaths_') state.players[offset].deaths = parseInt(item); - if(mode == 'pid_') state.players[offset].pid = item; - offset++; - } - } - } - - if('hostname' in state.raw) state.name = state.raw.hostname; - else if('servername' in state.raw) state.name = state.raw.servername; - if('mapname' in state.raw) state.map = state.raw.mapname; - if(state.raw.password == '1') state.password = true; - if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); - - self.finish(state); - } - ]); - }, - sendPacket: function(type,challenge,payload,assemble,c) { - var self = this; - - var challengeLength = (this.noChallenge || challenge === false) ? 0 : 4; - var payloadLength = payload ? payload.length : 0; - - var b = new Buffer(7 + challengeLength + payloadLength); - b.writeUInt8(0xFE, 0); - b.writeUInt8(0xFD, 1); - b.writeUInt8(type, 2); - b.writeUInt32BE(this.sessionId, 3); - if(challengeLength) b.writeInt32BE(challenge, 7); - if(payloadLength) payload.copy(b, 7+challengeLength); - - var numPackets = 0; - var packets = {}; - this.udpSend(b,function(buffer) { - var reader = self.reader(buffer); - var iType = reader.uint(1); - if(iType != type) return; - var iSessionId = reader.uint(4); - if(iSessionId != self.sessionId) return; - - if(!assemble) { - c(reader.rest()); - return true; - } - if(self.useOnlySingleSplit) { - // has split headers, but they are worthless and only one packet is used - reader.skip(11); - c([reader.rest()]); - return true; - } - - reader.skip(9); // filler data -- usually set to 'splitnum\0' - var id = reader.uint(1); - var last = (id & 0x80); - id = id & 0x7f; - if(last) numPackets = id+1; - - reader.skip(1); // "another 'packet number' byte, but isn't understood." - - packets[id] = reader.rest(); - if(self.debug) { - console.log("Received packet #"+id); - if(last) console.log("(last)"); - } - - if(!numPackets || Object.keys(packets).length != numPackets) return; - - // assemble the parts - var list = []; - for(var i = 0; i < numPackets; i++) { - if(!(i in packets)) { - self.fatal('Missing packet #'+i); - return true; - } - list.push(packets[i]); - } - c(list); - return true; - }); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.sessionId = 1; + this.encoding = 'latin1'; + this.byteorder = 'be'; + this.noChallenge = false; + this.useOnlySingleSplit = false; + }, + run: function(state) { + var self = this; + var challenge,packets; + + async.series([ + function(c) { + if(self.noChallenge) return c(); + self.sendPacket(9,false,false,false,function(buffer) { + var reader = self.reader(buffer); + challenge = parseInt(reader.string()); + c(); + }); + }, + function(c) { + self.sendPacket(0,challenge,new Buffer([0xff,0xff,0xff,0x01]),true,function(b) { + packets = b; + c(); + }); + }, + function(c) { + // iterate over the received packets + // the first packet will start off with k/v pairs, followed with data fields + // the following packets will only have data fields + var data = {}; + + for(var iPacket = 0; iPacket < packets.length; iPacket++) { + var packet = packets[iPacket]; + var reader = self.reader(packet); + + if(self.debug) { + console.log("+++"+packet.toString('hex')); + console.log(":::"+packet.toString('ascii')); + } + + if(iPacket == 0) { + while(!reader.done()) { + var key = reader.string(); + if(!key) break; + var value = reader.string(); + + // reread the next line if we hit the weird ut3 bug + if(value == 'p1073741829') value = reader.string(); + + state.raw[key] = value; + } + } + + var firstMode = true; + while(!reader.done()) { + var mode = reader.string(); + if(mode.charCodeAt(0) <= 2) mode = mode.substring(1); + if(!mode) continue; + var offset = 0; + if(iPacket != 0 && firstMode) offset = reader.uint(1); + reader.skip(1); + firstMode = false; + + while(!reader.done()) { + var item = reader.string(); + if(!item) break; + + if( + mode == 'player_' + || mode == 'score_' + || mode == 'ping_' + || mode == 'team_' + || mode == 'deaths_' + || mode == 'pid_' + ) { + if(state.players.length <= offset) + state.players.push({}); + } + if(mode == 'player_') state.players[offset].name = item; + if(mode == 'score_') state.players[offset].score = parseInt(item); + if(mode == 'ping_') state.players[offset].ping = parseInt(item); + if(mode == 'team_') state.players[offset].team = parseInt(item); + if(mode == 'deaths_') state.players[offset].deaths = parseInt(item); + if(mode == 'pid_') state.players[offset].pid = item; + offset++; + } + } + } + + if('hostname' in state.raw) state.name = state.raw.hostname; + else if('servername' in state.raw) state.name = state.raw.servername; + if('mapname' in state.raw) state.map = state.raw.mapname; + if(state.raw.password == '1') state.password = true; + if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); + + self.finish(state); + } + ]); + }, + sendPacket: function(type,challenge,payload,assemble,c) { + var self = this; + + var challengeLength = (this.noChallenge || challenge === false) ? 0 : 4; + var payloadLength = payload ? payload.length : 0; + + var b = new Buffer(7 + challengeLength + payloadLength); + b.writeUInt8(0xFE, 0); + b.writeUInt8(0xFD, 1); + b.writeUInt8(type, 2); + b.writeUInt32BE(this.sessionId, 3); + if(challengeLength) b.writeInt32BE(challenge, 7); + if(payloadLength) payload.copy(b, 7+challengeLength); + + var numPackets = 0; + var packets = {}; + this.udpSend(b,function(buffer) { + var reader = self.reader(buffer); + var iType = reader.uint(1); + if(iType != type) return; + var iSessionId = reader.uint(4); + if(iSessionId != self.sessionId) return; + + if(!assemble) { + c(reader.rest()); + return true; + } + if(self.useOnlySingleSplit) { + // has split headers, but they are worthless and only one packet is used + reader.skip(11); + c([reader.rest()]); + return true; + } + + reader.skip(9); // filler data -- usually set to 'splitnum\0' + var id = reader.uint(1); + var last = (id & 0x80); + id = id & 0x7f; + if(last) numPackets = id+1; + + reader.skip(1); // "another 'packet number' byte, but isn't understood." + + packets[id] = reader.rest(); + if(self.debug) { + console.log("Received packet #"+id); + if(last) console.log("(last)"); + } + + if(!numPackets || Object.keys(packets).length != numPackets) return; + + // assemble the parts + var list = []; + for(var i = 0; i < numPackets; i++) { + if(!(i in packets)) { + self.fatal('Missing packet #'+i); + return true; + } + list.push(packets[i]); + } + c(list); + return true; + }); + } +}); diff --git a/protocols/hexenworld.js b/protocols/hexenworld.js index 1524e28..5153b72 100644 --- a/protocols/hexenworld.js +++ b/protocols/hexenworld.js @@ -1,6 +1,6 @@ -module.exports = require('./quake2').extend({ - init: function() { - this._super(); - this.sendHeader = '\xFFstatus\x0a'; - } -}); +module.exports = require('./quake2').extend({ + init: function() { + this._super(); + this.sendHeader = '\xFFstatus\x0a'; + } +}); diff --git a/protocols/jc2mp.js b/protocols/jc2mp.js index 545dc54..a76f0f6 100644 --- a/protocols/jc2mp.js +++ b/protocols/jc2mp.js @@ -1,24 +1,24 @@ -/* -module.exports = require('./valve').extend({ - init: function() { - this._super(); - } -}); -*/ - -// supposedly, gamespy3 is the "official" query protocol for jcmp, -// but it's broken (requires useOnlySingleSplit), and doesn't include player names -module.exports = require('./gamespy3').extend({ - init: function() { - this._super(); - this.useOnlySingleSplit = true; - }, - finalizeState: function(state) { - this._super(state); - if(!state.players.length && parseInt(state.raw.numplayers)) { - for(var i = 0; i < parseInt(state.raw.numplayers); i++) { - state.players.push({}); - } - } - } -}); +/* +module.exports = require('./valve').extend({ + init: function() { + this._super(); + } +}); +*/ + +// supposedly, gamespy3 is the "official" query protocol for jcmp, +// but it's broken (requires useOnlySingleSplit), and doesn't include player names +module.exports = require('./gamespy3').extend({ + init: function() { + this._super(); + this.useOnlySingleSplit = true; + }, + finalizeState: function(state) { + this._super(state); + if(!state.players.length && parseInt(state.raw.numplayers)) { + for(var i = 0; i < parseInt(state.raw.numplayers); i++) { + state.players.push({}); + } + } + } +}); diff --git a/protocols/killingfloor.js b/protocols/killingfloor.js index 4b82b96..44d2d87 100644 --- a/protocols/killingfloor.js +++ b/protocols/killingfloor.js @@ -1,6 +1,6 @@ -module.exports = require('./unreal2').extend({ - readExtraInfo: function(reader,state) { - state.raw.wavecurrent = reader.uint(4); - state.raw.wavetotal = reader.uint(4); - } -}); +module.exports = require('./unreal2').extend({ + readExtraInfo: function(reader,state) { + state.raw.wavecurrent = reader.uint(4); + state.raw.wavetotal = reader.uint(4); + } +}); diff --git a/protocols/m2mp.js b/protocols/m2mp.js index 8125301..5522a14 100644 --- a/protocols/m2mp.js +++ b/protocols/m2mp.js @@ -1,37 +1,37 @@ -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.encoding = 'latin1'; - }, - run: function(state) { - var self = this; - - this.udpSend('M2MP',function(buffer) { - var reader = self.reader(buffer); - - var header = reader.string({length:4}); - if(header != 'M2MP') return; - - state.name = self.readString(reader); - state.raw.numplayers = self.readString(reader); - state.maxplayers = self.readString(reader); - state.raw.gamemode = self.readString(reader); - state.password = !!reader.uint(1); - - while(!reader.done()) { - var name = self.readString(reader); - if(!name) break; - state.players.push({ - name:name - }); - } - - self.finish(state); - return true; - }); - }, - readString: function(reader) { - var length = reader.uint(1); - return reader.string({length:length-1}); - }, -}); +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.encoding = 'latin1'; + }, + run: function(state) { + var self = this; + + this.udpSend('M2MP',function(buffer) { + var reader = self.reader(buffer); + + var header = reader.string({length:4}); + if(header != 'M2MP') return; + + state.name = self.readString(reader); + state.raw.numplayers = self.readString(reader); + state.maxplayers = self.readString(reader); + state.raw.gamemode = self.readString(reader); + state.password = !!reader.uint(1); + + while(!reader.done()) { + var name = self.readString(reader); + if(!name) break; + state.players.push({ + name:name + }); + } + + self.finish(state); + return true; + }); + }, + readString: function(reader) { + var length = reader.uint(1); + return reader.string({length:length-1}); + }, +}); diff --git a/protocols/minecraftping.js b/protocols/minecraftping.js index 1e1d2b0..150312f 100644 --- a/protocols/minecraftping.js +++ b/protocols/minecraftping.js @@ -1,93 +1,93 @@ -var varint = require('varint'), - async = require('async'); - -function varIntBuffer(num) { - return new Buffer(varint.encode(num)); -} -function buildPacket(id,data) { - if(!data) data = new Buffer(0); - var idBuffer = varIntBuffer(id); - return Buffer.concat([ - varIntBuffer(data.length+idBuffer.length), - idBuffer, - data - ]); -} - -module.exports = require('./core').extend({ - run: function(state) { - var self = this; - var receivedData; - - async.series([ - function(c) { - // build and send handshake and status TCP packet - - var portBuf = new Buffer(2); - portBuf.writeUInt16BE(self.options.port_query,0); - - var addressBuf = new Buffer(self.options.address,'utf8'); - - var bufs = [ - varIntBuffer(4), - varIntBuffer(addressBuf.length), - addressBuf, - portBuf, - varIntBuffer(1) - ]; - - var outBuffer = Buffer.concat([ - buildPacket(0,Buffer.concat(bufs)), - buildPacket(0) - ]); - - self.tcpSend(outBuffer, function(data) { - if(data.length < 10) return false; - var expected = varint.decode(data); - data = data.slice(varint.decode.bytesRead); - if(data.length < expected) return false; - receivedData = data; - c(); - return true; - }); - }, - function(c) { - // parse response - - var data = receivedData; - var packetId = varint.decode(data); - data = data.slice(varint.decode.bytesRead); - - var strLen = varint.decode(data); - data = data.slice(varint.decode.bytesRead); - - var str = data.toString('utf8'); - var json; - try { - json = JSON.parse(str); - delete json.favicon; - if(self.debug) console.log(json); - } catch(e) { - return self.fatal('Invalid JSON'); - } - - state.raw.version = json.version.name; - state.maxplayers = json.players.max; - state.raw.description = json.description.text; - if(json.players.sample) { - for(var i = 0; i < json.players.sample.length; i++) { - state.players.push({ - id: json.players.sample[i].id, - name: json.players.sample[i].name - }); - } - } - while(state.players.length < json.players.online) { - state.players.push({}); - } - - self.finish(state); - } - ]); - } -}); +var varint = require('varint'), + async = require('async'); + +function varIntBuffer(num) { + return new Buffer(varint.encode(num)); +} +function buildPacket(id,data) { + if(!data) data = new Buffer(0); + var idBuffer = varIntBuffer(id); + return Buffer.concat([ + varIntBuffer(data.length+idBuffer.length), + idBuffer, + data + ]); +} + +module.exports = require('./core').extend({ + run: function(state) { + var self = this; + var receivedData; + + async.series([ + function(c) { + // build and send handshake and status TCP packet + + var portBuf = new Buffer(2); + portBuf.writeUInt16BE(self.options.port_query,0); + + var addressBuf = new Buffer(self.options.address,'utf8'); + + var bufs = [ + varIntBuffer(4), + varIntBuffer(addressBuf.length), + addressBuf, + portBuf, + varIntBuffer(1) + ]; + + var outBuffer = Buffer.concat([ + buildPacket(0,Buffer.concat(bufs)), + buildPacket(0) + ]); + + self.tcpSend(outBuffer, function(data) { + if(data.length < 10) return false; + var expected = varint.decode(data); + data = data.slice(varint.decode.bytesRead); + if(data.length < expected) return false; + receivedData = data; + c(); + return true; + }); + }, + function(c) { + // parse response + + var data = receivedData; + var packetId = varint.decode(data); + data = data.slice(varint.decode.bytesRead); + + var strLen = varint.decode(data); + data = data.slice(varint.decode.bytesRead); + + var str = data.toString('utf8'); + var json; + try { + json = JSON.parse(str); + delete json.favicon; + if(self.debug) console.log(json); + } catch(e) { + return self.fatal('Invalid JSON'); + } + + state.raw.version = json.version.name; + state.maxplayers = json.players.max; + state.raw.description = json.description.text; + if(json.players.sample) { + for(var i = 0; i < json.players.sample.length; i++) { + state.players.push({ + id: json.players.sample[i].id, + name: json.players.sample[i].name + }); + } + } + while(state.players.length < json.players.online) { + state.players.push({}); + } + + self.finish(state); + } + ]); + } +}); diff --git a/protocols/mumble.js b/protocols/mumble.js index 2ddb313..c4313fd 100644 --- a/protocols/mumble.js +++ b/protocols/mumble.js @@ -1,44 +1,44 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.options.tcpTimeout = 5000; - }, - run: function(state) { - var self = this; - - this.tcpSend('json', function(buffer) { - if(buffer.length < 10) return; - var str = buffer.toString(); - var json; - try { - json = JSON.parse(str); - } catch(e) { - // probably not all here yet - return; - } - - state.raw = json; - state.name = json.name; - - var channelStack = [state.raw.root]; - while(channelStack.length) { - var channel = channelStack.shift(); - channel.description = self.cleanComment(channel.description); - channelStack = channelStack.concat(channel.channels); - for(var i = 0; i < channel.users.length; i++) { - var user = channel.users[i]; - user.comment = self.cleanComment(user.comment); - state.players.push(user); - } - } - - self.finish(state); - return true; - }); - }, - cleanComment: function(str) { - return str.replace(/<.*>/g,''); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.options.tcpTimeout = 5000; + }, + run: function(state) { + var self = this; + + this.tcpSend('json', function(buffer) { + if(buffer.length < 10) return; + var str = buffer.toString(); + var json; + try { + json = JSON.parse(str); + } catch(e) { + // probably not all here yet + return; + } + + state.raw = json; + state.name = json.name; + + var channelStack = [state.raw.root]; + while(channelStack.length) { + var channel = channelStack.shift(); + channel.description = self.cleanComment(channel.description); + channelStack = channelStack.concat(channel.channels); + for(var i = 0; i < channel.users.length; i++) { + var user = channel.users[i]; + user.comment = self.cleanComment(user.comment); + state.players.push(user); + } + } + + self.finish(state); + return true; + }); + }, + cleanComment: function(str) { + return str.replace(/<.*>/g,''); + } +}); diff --git a/protocols/mumbleping.js b/protocols/mumbleping.js index c0ef63b..acac0b8 100644 --- a/protocols/mumbleping.js +++ b/protocols/mumbleping.js @@ -1,29 +1,29 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.byteorder = 'be'; - }, - run: function(state) { - var self = this; - - this.udpSend('\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08', function(buffer) { - if(buffer.length < 24) return; - var reader = self.reader(buffer); - reader.skip(1); - state.raw.versionMajor = reader.uint(1); - state.raw.versionMinor = reader.uint(1); - state.raw.versionPatch = reader.uint(1); - reader.skip(8); - state.raw.numplayers = reader.uint(4); - state.maxplayers = reader.uint(4); - state.raw.allowedbandwidth = reader.uint(4); - for(var i = 0; i < state.raw.numplayers; i++) { - state.players.push({}); - } - self.finish(state); - return true; - }); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.byteorder = 'be'; + }, + run: function(state) { + var self = this; + + this.udpSend('\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08', function(buffer) { + if(buffer.length < 24) return; + var reader = self.reader(buffer); + reader.skip(1); + state.raw.versionMajor = reader.uint(1); + state.raw.versionMinor = reader.uint(1); + state.raw.versionPatch = reader.uint(1); + reader.skip(8); + state.raw.numplayers = reader.uint(4); + state.maxplayers = reader.uint(4); + state.raw.allowedbandwidth = reader.uint(4); + for(var i = 0; i < state.raw.numplayers; i++) { + state.players.push({}); + } + self.finish(state); + return true; + }); + } +}); diff --git a/protocols/mutantfactions.js b/protocols/mutantfactions.js index 53ccc8e..bfff46a 100644 --- a/protocols/mutantfactions.js +++ b/protocols/mutantfactions.js @@ -1,54 +1,54 @@ -var request = require('request'); - -module.exports = require('./protocols/core').extend({ - run: function(state) { - var self = this; - request({ - uri: 'http://mutantfactions.net/game/receiveLobby.php', - timeout: 3000, - }, function(e,r,body) { - if(e) return self.fatal('Lobby request error'); - - var split = body.split('
'); - - var found = false; - for(var i = 0; i < split.length; i++) { - var line = split[i]; - var fields = line.split('::'); - var ip = fields[2]; - var port = fields[3]; - if(ip == self.options.address && port == self.options.port) { - found = fields; - break; - } - } - - if(!found) return self.fatal('Server not found in list'); - - state.raw.countrycode = fields[0]; - state.raw.country = fields[1]; - state.name = fields[4]; - state.map = fields[5]; - state.raw.numplayers = fields[6]; - state.maxplayers = fields[7]; - // fields[8] is unknown? - state.raw.rules = fields[9]; - state.raw.gamemode = fields[10]; - state.raw.gangsters = fields[11]; - state.raw.cashrate = fields[12]; - state.raw.missions = fields[13]; - state.raw.vehicles = fields[14]; - state.raw.customweapons = fields[15]; - state.raw.friendlyfire = fields[16]; - state.raw.mercs = fields[17]; - // fields[18] is unknown? listen server? - state.raw.version = fields[19]; - - for(var i = 0; i < state.raw.numplayers; i++) { - state.players.push({}); - } - - self.finish(state); - }); - } -}); +var request = require('request'); + +module.exports = require('./protocols/core').extend({ + run: function(state) { + var self = this; + request({ + uri: 'http://mutantfactions.net/game/receiveLobby.php', + timeout: 3000, + }, function(e,r,body) { + if(e) return self.fatal('Lobby request error'); + + var split = body.split('
'); + + var found = false; + for(var i = 0; i < split.length; i++) { + var line = split[i]; + var fields = line.split('::'); + var ip = fields[2]; + var port = fields[3]; + if(ip == self.options.address && port == self.options.port) { + found = fields; + break; + } + } + + if(!found) return self.fatal('Server not found in list'); + + state.raw.countrycode = fields[0]; + state.raw.country = fields[1]; + state.name = fields[4]; + state.map = fields[5]; + state.raw.numplayers = fields[6]; + state.maxplayers = fields[7]; + // fields[8] is unknown? + state.raw.rules = fields[9]; + state.raw.gamemode = fields[10]; + state.raw.gangsters = fields[11]; + state.raw.cashrate = fields[12]; + state.raw.missions = fields[13]; + state.raw.vehicles = fields[14]; + state.raw.customweapons = fields[15]; + state.raw.friendlyfire = fields[16]; + state.raw.mercs = fields[17]; + // fields[18] is unknown? listen server? + state.raw.version = fields[19]; + + for(var i = 0; i < state.raw.numplayers; i++) { + state.players.push({}); + } + + self.finish(state); + }); + } +}); diff --git a/protocols/nadeo.js b/protocols/nadeo.js index 2cb6791..d3c6ff0 100644 --- a/protocols/nadeo.js +++ b/protocols/nadeo.js @@ -1,75 +1,75 @@ -var gbxremote = require('gbxremote'), - async = require('async'); - -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.options.port = 2350; - this.options.port_query = 5000; - this.gbxclient = false; - }, - reset: function() { - this._super(); - if(this.gbxclient) { - this.gbxclient.terminate(); - this.gbxclient = false; - } - }, - run: function(state) { - var self = this; - - var cmds = [ - ['Connect'], - ['Authenticate', this.options.login,this.options.password], - ['GetStatus'], - ['GetPlayerList',500,0], - ['GetServerOptions'], - ['GetCurrentChallengeInfo'], - ['GetCurrentGameInfo'] - ]; - var results = []; - - async.eachSeries(cmds, function(cmdset,c) { - var cmd = cmdset[0]; - var params = cmdset.slice(1); - - if(cmd == 'Connect') { - var client = self.gbxclient = gbxremote.createClient(self.options.port_query,self.options.host, function(err) { - if(err) return self.fatal('GBX error '+JSON.stringify(err)); - c(); - }); - client.on('error',function(){}); - } else { - self.gbxclient.methodCall(cmd, params, function(err, value) { - if(err) return self.fatal('XMLRPC error '+JSON.stringify(err)); - results.push(value); - c(); - }); - } - }, function() { - var gamemode = ''; - var igm = results[5].GameMode; - if(igm == 0) gamemode="Rounds"; - if(igm == 1) gamemode="Time Attack"; - if(igm == 2) gamemode="Team"; - if(igm == 3) gamemode="Laps"; - if(igm == 4) gamemode="Stunts"; - if(igm == 5) gamemode="Cup"; - - state.name = self.stripColors(results[3].Name); - state.password = (results[3].Password != 'No password'); - state.maxplayers = results[3].CurrentMaxPlayers; - state.map = self.stripColors(results[4].Name); - state.raw.gametype = gamemode; - - results[2].forEach(function(player) { - state.players.push({name:self.stripColors(player.Name)}); - }); - - self.finish(state); - }); - }, - stripColors: function(str) { - return str.replace(/\$([0-9a-f][^\$]?[^\$]?|[^\$]?)/g,''); - } -}); +var gbxremote = require('gbxremote'), + async = require('async'); + +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.options.port = 2350; + this.options.port_query = 5000; + this.gbxclient = false; + }, + reset: function() { + this._super(); + if(this.gbxclient) { + this.gbxclient.terminate(); + this.gbxclient = false; + } + }, + run: function(state) { + var self = this; + + var cmds = [ + ['Connect'], + ['Authenticate', this.options.login,this.options.password], + ['GetStatus'], + ['GetPlayerList',500,0], + ['GetServerOptions'], + ['GetCurrentChallengeInfo'], + ['GetCurrentGameInfo'] + ]; + var results = []; + + async.eachSeries(cmds, function(cmdset,c) { + var cmd = cmdset[0]; + var params = cmdset.slice(1); + + if(cmd == 'Connect') { + var client = self.gbxclient = gbxremote.createClient(self.options.port_query,self.options.host, function(err) { + if(err) return self.fatal('GBX error '+JSON.stringify(err)); + c(); + }); + client.on('error',function(){}); + } else { + self.gbxclient.methodCall(cmd, params, function(err, value) { + if(err) return self.fatal('XMLRPC error '+JSON.stringify(err)); + results.push(value); + c(); + }); + } + }, function() { + var gamemode = ''; + var igm = results[5].GameMode; + if(igm == 0) gamemode="Rounds"; + if(igm == 1) gamemode="Time Attack"; + if(igm == 2) gamemode="Team"; + if(igm == 3) gamemode="Laps"; + if(igm == 4) gamemode="Stunts"; + if(igm == 5) gamemode="Cup"; + + state.name = self.stripColors(results[3].Name); + state.password = (results[3].Password != 'No password'); + state.maxplayers = results[3].CurrentMaxPlayers; + state.map = self.stripColors(results[4].Name); + state.raw.gametype = gamemode; + + results[2].forEach(function(player) { + state.players.push({name:self.stripColors(player.Name)}); + }); + + self.finish(state); + }); + }, + stripColors: function(str) { + return str.replace(/\$([0-9a-f][^\$]?[^\$]?|[^\$]?)/g,''); + } +}); diff --git a/protocols/quake1.js b/protocols/quake1.js index fe338e4..4b18c9a 100644 --- a/protocols/quake1.js +++ b/protocols/quake1.js @@ -1,7 +1,7 @@ -module.exports = require('./quake2').extend({ - init: function() { - this._super(); - this.responseHeader = 'n'; - this.isQuake1 = true; - } -}); +module.exports = require('./quake2').extend({ + init: function() { + this._super(); + this.responseHeader = 'n'; + this.isQuake1 = true; + } +}); diff --git a/protocols/quake2.js b/protocols/quake2.js index 3fac1bb..802ad87 100644 --- a/protocols/quake2.js +++ b/protocols/quake2.js @@ -1,85 +1,85 @@ -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.encoding = 'latin1'; - this.delimiter = '\n'; - this.sendHeader = 'status'; - this.responseHeader = 'print'; - this.isQuake1 = false; - }, - run: function(state) { - var self = this; - - this.udpSend('\xff\xff\xff\xff'+this.sendHeader+'\x00',function(buffer) { - var reader = self.reader(buffer); - - var header = reader.string({length:4}); - if(header != '\xff\xff\xff\xff') return; - - var response; - if(this.isQuake1) { - response = reader.string({length:1}); - } else { - response = reader.string(); - } - if(response != this.responseHeader) return; - - var info = reader.string().split('\\'); - if(info[0] == '') info.shift(); - - while(true) { - var key = info.shift(); - var value = info.shift(); - if(typeof value == 'undefined') break; - state.raw[key] = value; - } - - while(!reader.done()) { - var line = reader.string(); - if(!line || line.charAt(0) == '\0') break; - - var args = []; - var split = line.split('"'); - var inQuote = false; - split.forEach(function(part,i) { - var inQuote = (i%2 == 1); - if(inQuote) { - args.push(part); - } else { - var splitSpace = part.split(' '); - splitSpace.forEach(function(subpart) { - if(subpart) args.push(subpart); - }); - } - }); - - var player = {}; - if(self.isQuake1) { - player.id = parseInt(args.shift()); - player.score = parseInt(args.shift()); - player.time = parseInt(args.shift()); - player.ping = parseInt(args.shift()); - player.name = args.shift(); - player.skin = args.shift(); - player.color1 = parseInt(args.shift()); - player.color2 = parseInt(args.shift()); - } else { - player.frags = parseInt(args.shift()); - player.ping = parseInt(args.shift()); - player.name = args.shift() || ''; - player.address = args.shift() || ''; - } - - (player.ping ? state.players : state.bots).push(player); - } - - if('g_needpass' in state.raw) state.password = state.raw.g_needpass; - if('mapname' in state.raw) state.map = state.raw.mapname; - if('sv_maxclients' in state.raw) state.maxplayers = state.raw.sv_maxclients; - if('sv_hostname' in state.raw) state.name = state.raw.sv_hostname; - - self.finish(state); - return true; - }); - } -}); +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.encoding = 'latin1'; + this.delimiter = '\n'; + this.sendHeader = 'status'; + this.responseHeader = 'print'; + this.isQuake1 = false; + }, + run: function(state) { + var self = this; + + this.udpSend('\xff\xff\xff\xff'+this.sendHeader+'\x00',function(buffer) { + var reader = self.reader(buffer); + + var header = reader.string({length:4}); + if(header != '\xff\xff\xff\xff') return; + + var response; + if(this.isQuake1) { + response = reader.string({length:1}); + } else { + response = reader.string(); + } + if(response != this.responseHeader) return; + + var info = reader.string().split('\\'); + if(info[0] == '') info.shift(); + + while(true) { + var key = info.shift(); + var value = info.shift(); + if(typeof value == 'undefined') break; + state.raw[key] = value; + } + + while(!reader.done()) { + var line = reader.string(); + if(!line || line.charAt(0) == '\0') break; + + var args = []; + var split = line.split('"'); + var inQuote = false; + split.forEach(function(part,i) { + var inQuote = (i%2 == 1); + if(inQuote) { + args.push(part); + } else { + var splitSpace = part.split(' '); + splitSpace.forEach(function(subpart) { + if(subpart) args.push(subpart); + }); + } + }); + + var player = {}; + if(self.isQuake1) { + player.id = parseInt(args.shift()); + player.score = parseInt(args.shift()); + player.time = parseInt(args.shift()); + player.ping = parseInt(args.shift()); + player.name = args.shift(); + player.skin = args.shift(); + player.color1 = parseInt(args.shift()); + player.color2 = parseInt(args.shift()); + } else { + player.frags = parseInt(args.shift()); + player.ping = parseInt(args.shift()); + player.name = args.shift() || ''; + player.address = args.shift() || ''; + } + + (player.ping ? state.players : state.bots).push(player); + } + + if('g_needpass' in state.raw) state.password = state.raw.g_needpass; + if('mapname' in state.raw) state.map = state.raw.mapname; + if('sv_maxclients' in state.raw) state.maxplayers = state.raw.sv_maxclients; + if('sv_hostname' in state.raw) state.name = state.raw.sv_hostname; + + self.finish(state); + return true; + }); + } +}); diff --git a/protocols/quake3.js b/protocols/quake3.js index 90c2677..7fc7990 100644 --- a/protocols/quake3.js +++ b/protocols/quake3.js @@ -1,19 +1,19 @@ -module.exports = require('./quake2').extend({ - init: function() { - this._super(); - this.sendHeader = 'getstatus'; - this.responseHeader = 'statusResponse'; - }, - finalizeState: function(state) { - state.name = this.stripColors(state.name); - for(var i in state.raw) { - state.raw[i] = this.stripColors(state.raw[i]); - } - for(var i = 0; i < state.players.length; i++) { - state.players[i].name = this.stripColors(state.players[i].name); - } - }, - stripColors: function(str) { - return str.replace(/\^(X.{6}|.)/g,''); - } -}); +module.exports = require('./quake2').extend({ + init: function() { + this._super(); + this.sendHeader = 'getstatus'; + this.responseHeader = 'statusResponse'; + }, + finalizeState: function(state) { + state.name = this.stripColors(state.name); + for(var i in state.raw) { + state.raw[i] = this.stripColors(state.raw[i]); + } + for(var i = 0; i < state.players.length; i++) { + state.players[i].name = this.stripColors(state.players[i].name); + } + }, + stripColors: function(str) { + return str.replace(/\^(X.{6}|.)/g,''); + } +}); diff --git a/protocols/samp.js b/protocols/samp.js index d440038..8c620ad 100644 --- a/protocols/samp.js +++ b/protocols/samp.js @@ -1,90 +1,90 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - run: function(state) { - - var self = this; - var len; - - async.series([ - function(c) { - self.sendPacket('i',function(reader) { - state.password = !!reader.uint(1); - state.raw.numplayers = reader.uint(2); - state.maxplayers = reader.uint(2); - state.name = self.readString(reader,4); - state.raw.gamemode = self.readString(reader,4); - self.map = self.readString(reader,4); - c(); - }); - }, - function(c) { - self.sendPacket('r',function(reader) { - var ruleCount = reader.uint(2); - state.raw.rules = {}; - for(var i = 0; i < ruleCount; i++) { - var key = self.readString(reader,1); - var value = self.readString(reader,1); - state.raw.rules[key] = value; - } - if('mapname' in state.raw.rules) - state.map = state.raw.rules.mapname; - c(); - }); - }, - function(c) { - self.sendPacket('d',function(reader) { - var playerCount = reader.uint(2); - for(var i = 0; i < playerCount; i++) { - var player = {}; - player.id = reader.uint(1); - player.name = self.readString(reader,1); - player.score = reader.int(4); - player.ping = reader.uint(4); - state.players.push(player); - } - c(); - },function() { - for(var i = 0; i < state.raw.numplayers; i++) { - state.players.push({}); - } - }); - }, - function(c) { - self.finish(state); - } - ]); - }, - readString: function(reader,lenBytes) { - var length = reader.uint(lenBytes); - if(!length) return ''; - var string = reader.string({length:length}); - return string; - }, - sendPacket: function(type,onresponse,ontimeout) { - var self = this; - var outbuffer = new Buffer(11); - outbuffer.writeUInt32BE(0x53414D50,0); - var ipSplit = self.options.address.split('.'); - outbuffer.writeUInt8(parseInt(ipSplit[0]),4); - outbuffer.writeUInt8(parseInt(ipSplit[1]),5); - outbuffer.writeUInt8(parseInt(ipSplit[2]),6); - outbuffer.writeUInt8(parseInt(ipSplit[3]),7); - outbuffer.writeUInt16LE(self.options.port,8); - outbuffer.writeUInt8(type.charCodeAt(0),10); - - this.udpSend(outbuffer,function(buffer) { - var reader = self.reader(buffer); - for(var i = 0; i < outbuffer.length; i++) { - if(outbuffer.readUInt8(i) !== reader.uint(1)) return; - } - onresponse(reader); - return true; - },function() { - if(ontimeout) { - ontimeout(); - return true; - } - }); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + run: function(state) { + + var self = this; + var len; + + async.series([ + function(c) { + self.sendPacket('i',function(reader) { + state.password = !!reader.uint(1); + state.raw.numplayers = reader.uint(2); + state.maxplayers = reader.uint(2); + state.name = self.readString(reader,4); + state.raw.gamemode = self.readString(reader,4); + self.map = self.readString(reader,4); + c(); + }); + }, + function(c) { + self.sendPacket('r',function(reader) { + var ruleCount = reader.uint(2); + state.raw.rules = {}; + for(var i = 0; i < ruleCount; i++) { + var key = self.readString(reader,1); + var value = self.readString(reader,1); + state.raw.rules[key] = value; + } + if('mapname' in state.raw.rules) + state.map = state.raw.rules.mapname; + c(); + }); + }, + function(c) { + self.sendPacket('d',function(reader) { + var playerCount = reader.uint(2); + for(var i = 0; i < playerCount; i++) { + var player = {}; + player.id = reader.uint(1); + player.name = self.readString(reader,1); + player.score = reader.int(4); + player.ping = reader.uint(4); + state.players.push(player); + } + c(); + },function() { + for(var i = 0; i < state.raw.numplayers; i++) { + state.players.push({}); + } + }); + }, + function(c) { + self.finish(state); + } + ]); + }, + readString: function(reader,lenBytes) { + var length = reader.uint(lenBytes); + if(!length) return ''; + var string = reader.string({length:length}); + return string; + }, + sendPacket: function(type,onresponse,ontimeout) { + var self = this; + var outbuffer = new Buffer(11); + outbuffer.writeUInt32BE(0x53414D50,0); + var ipSplit = self.options.address.split('.'); + outbuffer.writeUInt8(parseInt(ipSplit[0]),4); + outbuffer.writeUInt8(parseInt(ipSplit[1]),5); + outbuffer.writeUInt8(parseInt(ipSplit[2]),6); + outbuffer.writeUInt8(parseInt(ipSplit[3]),7); + outbuffer.writeUInt16LE(self.options.port,8); + outbuffer.writeUInt8(type.charCodeAt(0),10); + + this.udpSend(outbuffer,function(buffer) { + var reader = self.reader(buffer); + for(var i = 0; i < outbuffer.length; i++) { + if(outbuffer.readUInt8(i) !== reader.uint(1)) return; + } + onresponse(reader); + return true; + },function() { + if(ontimeout) { + ontimeout(); + return true; + } + }); + } +}); diff --git a/protocols/teamspeak2.js b/protocols/teamspeak2.js index 8321a2c..a61e687 100644 --- a/protocols/teamspeak2.js +++ b/protocols/teamspeak2.js @@ -1,77 +1,77 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - run: function(state) { - var self = this; - - async.series([ - function(c) { - self.sendCommand('sel '+self.options.port, function(data) { - if(data != '[TS]') self.fatal('Invalid header'); - c(); - }); - }, - function(c) { - self.sendCommand('si', function(data) { - var split = data.split('\r\n'); - split.forEach(function(line) { - var equals = line.indexOf('='); - var key = equals == -1 ? line : line.substr(0,equals); - var value = equals == -1 ? '' : line.substr(equals+1); - state.raw[key] = value; - }); - c(); - }); - }, - function(c) { - self.sendCommand('pl', function(data) { - var split = data.split('\r\n'); - var fields = split.shift().split('\t'); - split.forEach(function(line) { - var split2 = line.split('\t'); - var player = {}; - split2.forEach(function(value,i) { - var key = fields[i]; - if(!key) return; - if(key == 'nick') key = 'name'; - if(m = value.match(/^"(.*)"$/)) value = m[1]; - player[key] = value; - }); - state.players.push(player); - }); - c(); - }); - }, - function(c) { - self.sendCommand('cl', function(data) { - var split = data.split('\r\n'); - var fields = split.shift().split('\t'); - state.raw.channels = []; - split.forEach(function(line) { - var split2 = line.split('\t'); - var channel = {}; - split2.forEach(function(value,i) { - var key = fields[i]; - if(!key) return; - if(m = value.match(/^"(.*)"$/)) value = m[1]; - channel[key] = value; - }); - state.raw.channels.push(channel); - }); - c(); - }); - }, - function(c) { - self.finish(state); - } - ]); - }, - sendCommand: function(cmd,c) { - this.tcpSend(cmd+'\x0A', function(buffer) { - if(buffer.length < 6) return; - if(buffer.slice(-6).toString() != '\r\nOK\r\n') return; - c(buffer.slice(0,-6).toString()); - return true; - }); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + run: function(state) { + var self = this; + + async.series([ + function(c) { + self.sendCommand('sel '+self.options.port, function(data) { + if(data != '[TS]') self.fatal('Invalid header'); + c(); + }); + }, + function(c) { + self.sendCommand('si', function(data) { + var split = data.split('\r\n'); + split.forEach(function(line) { + var equals = line.indexOf('='); + var key = equals == -1 ? line : line.substr(0,equals); + var value = equals == -1 ? '' : line.substr(equals+1); + state.raw[key] = value; + }); + c(); + }); + }, + function(c) { + self.sendCommand('pl', function(data) { + var split = data.split('\r\n'); + var fields = split.shift().split('\t'); + split.forEach(function(line) { + var split2 = line.split('\t'); + var player = {}; + split2.forEach(function(value,i) { + var key = fields[i]; + if(!key) return; + if(key == 'nick') key = 'name'; + if(m = value.match(/^"(.*)"$/)) value = m[1]; + player[key] = value; + }); + state.players.push(player); + }); + c(); + }); + }, + function(c) { + self.sendCommand('cl', function(data) { + var split = data.split('\r\n'); + var fields = split.shift().split('\t'); + state.raw.channels = []; + split.forEach(function(line) { + var split2 = line.split('\t'); + var channel = {}; + split2.forEach(function(value,i) { + var key = fields[i]; + if(!key) return; + if(m = value.match(/^"(.*)"$/)) value = m[1]; + channel[key] = value; + }); + state.raw.channels.push(channel); + }); + c(); + }); + }, + function(c) { + self.finish(state); + } + ]); + }, + sendCommand: function(cmd,c) { + this.tcpSend(cmd+'\x0A', function(buffer) { + if(buffer.length < 6) return; + if(buffer.slice(-6).toString() != '\r\nOK\r\n') return; + c(buffer.slice(0,-6).toString()); + return true; + }); + } +}); diff --git a/protocols/teamspeak3.js b/protocols/teamspeak3.js index fb4df2e..77597a0 100644 --- a/protocols/teamspeak3.js +++ b/protocols/teamspeak3.js @@ -1,74 +1,74 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - run: function(state) { - var self = this; - - async.series([ - function(c) { - self.sendCommand('use port='+self.options.port, function(data) { - var split = data.split('\n\r'); - if(split[0] != 'TS3') self.fatal('Invalid header'); - c(); - }, true); - }, - function(c) { - self.sendCommand('serverinfo', function(data) { - state.raw = data[0]; - c(); - }); - }, - function(c) { - self.sendCommand('clientlist', function(data) { - for(var i = 0; i < data.length; i++) { - data[i].name = data[i].client_nickname; - delete data[i].client_nickname; - state.players.push(data[i]); - } - c(); - }); - }, - function(c) { - self.sendCommand('channellist -topic', function(data) { - state.raw.channels = data; - c(); - }); - }, - function(c) { - self.finish(state); - } - ]); - }, - sendCommand: function(cmd,c,raw) { - this.tcpSend(cmd+'\x0A', function(buffer) { - if(buffer.length < 21) return; - if(buffer.slice(-21).toString() != '\n\rerror id=0 msg=ok\n\r') return; - var body = buffer.slice(0,-21).toString(); - - var out; - - if(raw) { - out = body; - } else { - var segments = body.split('|'); - out = []; - segments.forEach(function(line) { - var split = line.split(' '); - var unit = {}; - split.forEach(function(field) { - var equals = field.indexOf('='); - var key = equals == -1 ? field : field.substr(0,equals); - var value = equals == -1 ? '' : field.substr(equals+1) - .replace(/\\s/g,' ').replace(/\\\//g,'/'); - unit[key] = value; - }); - out.push(unit); - }); - } - - c(out); - - return true; - }); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + run: function(state) { + var self = this; + + async.series([ + function(c) { + self.sendCommand('use port='+self.options.port, function(data) { + var split = data.split('\n\r'); + if(split[0] != 'TS3') self.fatal('Invalid header'); + c(); + }, true); + }, + function(c) { + self.sendCommand('serverinfo', function(data) { + state.raw = data[0]; + c(); + }); + }, + function(c) { + self.sendCommand('clientlist', function(data) { + for(var i = 0; i < data.length; i++) { + data[i].name = data[i].client_nickname; + delete data[i].client_nickname; + state.players.push(data[i]); + } + c(); + }); + }, + function(c) { + self.sendCommand('channellist -topic', function(data) { + state.raw.channels = data; + c(); + }); + }, + function(c) { + self.finish(state); + } + ]); + }, + sendCommand: function(cmd,c,raw) { + this.tcpSend(cmd+'\x0A', function(buffer) { + if(buffer.length < 21) return; + if(buffer.slice(-21).toString() != '\n\rerror id=0 msg=ok\n\r') return; + var body = buffer.slice(0,-21).toString(); + + var out; + + if(raw) { + out = body; + } else { + var segments = body.split('|'); + out = []; + segments.forEach(function(line) { + var split = line.split(' '); + var unit = {}; + split.forEach(function(field) { + var equals = field.indexOf('='); + var key = equals == -1 ? field : field.substr(0,equals); + var value = equals == -1 ? '' : field.substr(equals+1) + .replace(/\\s/g,' ').replace(/\\\//g,'/'); + unit[key] = value; + }); + out.push(unit); + }); + } + + c(out); + + return true; + }); + } +}); diff --git a/protocols/terraria.js b/protocols/terraria.js index afa532b..e35bd80 100644 --- a/protocols/terraria.js +++ b/protocols/terraria.js @@ -1,35 +1,35 @@ -var request = require('request'); - -module.exports = require('./core').extend({ - run: function(state) { - var self = this; - request({ - uri: 'http://'+this.options.address+':'+this.options.port_query+'/v2/server/status', - timeout: 3000, - qs: { - players: 'true', - token: this.options.token - } - }, function(e,r,body) { - if(e) return self.fatal('HTTP error'); - var json; - try { - json = JSON.parse(body); - } catch(e) { - return self.fatal('Invalid JSON'); - } - - if(json.status != 200) return self.fatal('Invalid status'); - - json.players.forEach(function(one) { - state.players.push({name:one.nickname,team:one.team}); - }); - - state.name = json.name; - state.raw.port = json.port; - state.raw.numplayers = json.playercount; - - self.finish(state); - }); - } -}); +var request = require('request'); + +module.exports = require('./core').extend({ + run: function(state) { + var self = this; + request({ + uri: 'http://'+this.options.address+':'+this.options.port_query+'/v2/server/status', + timeout: 3000, + qs: { + players: 'true', + token: this.options.token + } + }, function(e,r,body) { + if(e) return self.fatal('HTTP error'); + var json; + try { + json = JSON.parse(body); + } catch(e) { + return self.fatal('Invalid JSON'); + } + + if(json.status != 200) return self.fatal('Invalid status'); + + json.players.forEach(function(one) { + state.players.push({name:one.nickname,team:one.team}); + }); + + state.name = json.name; + state.raw.port = json.port; + state.raw.numplayers = json.playercount; + + self.finish(state); + }); + } +}); diff --git a/protocols/unreal2.js b/protocols/unreal2.js index c7c24d7..b4ea301 100644 --- a/protocols/unreal2.js +++ b/protocols/unreal2.js @@ -1,142 +1,142 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.encoding = 'latin1'; - }, - run: function(state) { - - var self = this; - - async.series([ - function(c) { - self.sendPacket(0,true,function(b) { - var reader = self.reader(b); - state.raw.serverid = reader.uint(4); - state.raw.ip = self.readUnrealString(reader); - state.raw.port = reader.uint(4); - state.raw.queryport = reader.uint(4); - state.name = self.readUnrealString(reader,true); - state.map = self.readUnrealString(reader,true); - state.raw.gametype = self.readUnrealString(reader,true); - state.raw.numplayers = reader.uint(4); - state.maxplayers = reader.uint(4); - self.readExtraInfo(reader,state); - - c(); - }); - }, - function(c) { - self.sendPacket(1,true,function(b) { - var reader = self.reader(b); - state.raw.mutators = []; - state.raw.rules = {}; - while(!reader.done()) { - var key = self.readUnrealString(reader,true); - var value = self.readUnrealString(reader,true); - if(key == 'Mutator') state.raw.mutators.push(value); - else state.raw.rules[key] = value; - } - - if('GamePassword' in state.raw.rules) - state.password = state.raw.rules.GamePassword != 'True'; - - c(); - }); - }, - function(c) { - self.sendPacket(2,false,function(b) { - var reader = self.reader(b); - - while(!reader.done()) { - var player = {}; - player.id = reader.uint(4); - if(!player.id) break; - if(player.id == 0) { - // Unreal2XMP Player (ID is always 0) - reader.skip(4); - } - player.name = self.readUnrealString(reader,true); - player.ping = reader.uint(4); - player.score = reader.int(4); - reader.skip(4); // stats ID - - // Extra data for Unreal2XMP players - if(player.id == 0) { - var count = reader.uint(1); - for(var iField = 0; iField < count; iField++) { - var key = self.readUnrealString(reader,true); - var value = self.readUnrealString(reader,true); - player[key] = value; - } - } - - if(player.id == 0 && player.name == 'Player') { - // these show up in ut2004 queries, but aren't real - // not even really sure why they're there - continue; - } - - (player.ping ? state.players : state.bots).push(player); - } - c(); - }); - }, - function(c) { - self.finish(state); - } - ]); - }, - readExtraInfo: function(reader,state) { - if(this.debug) { - console.log("UNREAL2 EXTRA INFO:"); - console.log(reader.uint(4)); - console.log(reader.uint(4)); - console.log(reader.uint(4)); - console.log(reader.uint(4)); - console.log(reader.buffer.slice(reader.i)); - } - }, - readUnrealString: function(reader, stripColor) { - var length = reader.uint(1); - var out; - if(length < 0x80) { - //out = reader.string({length:length}); - out = ''; - if(length > 0) out = reader.string(); - } else { - length = (length&0x7f)*2; - if(this.debug) { - console.log("UCS2 STRING"); - console.log(length,reader.buffer.slice(reader.i,reader.i+length)); - } - out = reader.string({encoding:'ucs2',length:length}); - } - - if(out.charCodeAt(out.length-1) == 0) - out = out.substring(0,out.length-1); - - if(stripColor) - out = out.replace(/\x1b...|[\x00-\x1a]/g,''); - - return out; - }, - sendPacket: function(type,required,callback) { - var self = this; - var outbuffer = new Buffer([0x79,0,0,0,type]); - - var packets = []; - this.udpSend(outbuffer,function(buffer) { - var reader = self.reader(buffer); - var header = reader.uint(4); - var iType = reader.uint(1); - if(iType != type) return; - packets.push(reader.rest()); - },function() { - if(!packets.length && required) return; - callback(Buffer.concat(packets)); - return true; - }); - } -}); +var async = require('async'); + +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.encoding = 'latin1'; + }, + run: function(state) { + + var self = this; + + async.series([ + function(c) { + self.sendPacket(0,true,function(b) { + var reader = self.reader(b); + state.raw.serverid = reader.uint(4); + state.raw.ip = self.readUnrealString(reader); + state.raw.port = reader.uint(4); + state.raw.queryport = reader.uint(4); + state.name = self.readUnrealString(reader,true); + state.map = self.readUnrealString(reader,true); + state.raw.gametype = self.readUnrealString(reader,true); + state.raw.numplayers = reader.uint(4); + state.maxplayers = reader.uint(4); + self.readExtraInfo(reader,state); + + c(); + }); + }, + function(c) { + self.sendPacket(1,true,function(b) { + var reader = self.reader(b); + state.raw.mutators = []; + state.raw.rules = {}; + while(!reader.done()) { + var key = self.readUnrealString(reader,true); + var value = self.readUnrealString(reader,true); + if(key == 'Mutator') state.raw.mutators.push(value); + else state.raw.rules[key] = value; + } + + if('GamePassword' in state.raw.rules) + state.password = state.raw.rules.GamePassword != 'True'; + + c(); + }); + }, + function(c) { + self.sendPacket(2,false,function(b) { + var reader = self.reader(b); + + while(!reader.done()) { + var player = {}; + player.id = reader.uint(4); + if(!player.id) break; + if(player.id == 0) { + // Unreal2XMP Player (ID is always 0) + reader.skip(4); + } + player.name = self.readUnrealString(reader,true); + player.ping = reader.uint(4); + player.score = reader.int(4); + reader.skip(4); // stats ID + + // Extra data for Unreal2XMP players + if(player.id == 0) { + var count = reader.uint(1); + for(var iField = 0; iField < count; iField++) { + var key = self.readUnrealString(reader,true); + var value = self.readUnrealString(reader,true); + player[key] = value; + } + } + + if(player.id == 0 && player.name == 'Player') { + // these show up in ut2004 queries, but aren't real + // not even really sure why they're there + continue; + } + + (player.ping ? state.players : state.bots).push(player); + } + c(); + }); + }, + function(c) { + self.finish(state); + } + ]); + }, + readExtraInfo: function(reader,state) { + if(this.debug) { + console.log("UNREAL2 EXTRA INFO:"); + console.log(reader.uint(4)); + console.log(reader.uint(4)); + console.log(reader.uint(4)); + console.log(reader.uint(4)); + console.log(reader.buffer.slice(reader.i)); + } + }, + readUnrealString: function(reader, stripColor) { + var length = reader.uint(1); + var out; + if(length < 0x80) { + //out = reader.string({length:length}); + out = ''; + if(length > 0) out = reader.string(); + } else { + length = (length&0x7f)*2; + if(this.debug) { + console.log("UCS2 STRING"); + console.log(length,reader.buffer.slice(reader.i,reader.i+length)); + } + out = reader.string({encoding:'ucs2',length:length}); + } + + if(out.charCodeAt(out.length-1) == 0) + out = out.substring(0,out.length-1); + + if(stripColor) + out = out.replace(/\x1b...|[\x00-\x1a]/g,''); + + return out; + }, + sendPacket: function(type,required,callback) { + var self = this; + var outbuffer = new Buffer([0x79,0,0,0,type]); + + var packets = []; + this.udpSend(outbuffer,function(buffer) { + var reader = self.reader(buffer); + var header = reader.uint(4); + var iType = reader.uint(1); + if(iType != type) return; + packets.push(reader.rest()); + },function() { + if(!packets.length && required) return; + callback(Buffer.concat(packets)); + return true; + }); + } +}); diff --git a/protocols/ut2004.js b/protocols/ut2004.js index 784101a..e1e32e2 100644 --- a/protocols/ut2004.js +++ b/protocols/ut2004.js @@ -1,7 +1,7 @@ -module.exports = require('./unreal2').extend({ - readExtraInfo: function(reader,state) { - state.raw.ping = reader.uint(4); - state.raw.flags = reader.uint(4); - state.raw.skill = reader.uint(2); - } -}); +module.exports = require('./unreal2').extend({ + readExtraInfo: function(reader,state) { + state.raw.ping = reader.uint(4); + state.raw.flags = reader.uint(4); + state.raw.skill = reader.uint(2); + } +}); diff --git a/protocols/ut3.js b/protocols/ut3.js index b9a6631..9a605d0 100644 --- a/protocols/ut3.js +++ b/protocols/ut3.js @@ -1,43 +1,43 @@ -module.exports = require('./gamespy3').extend({ - finalizeState: function(state) { - this._super(state); - - this.translate(state.raw,{ - 'mapname': false, - 'p1073741825': 'map', - 'p1073741826': 'gametype', - 'p1073741827': 'servername', - 'p1073741828': 'custom_mutators', - 'gamemode': 'joininprogress', - 's32779': 'gamemode', - 's0': 'bot_skill', - 's6': 'pure_server', - 's7': 'password', - 's8': 'vs_bots', - 's10': 'force_respawn', - 'p268435704': 'frag_limit', - 'p268435705': 'time_limit', - 'p268435703': 'numbots', - 'p268435717': 'stock_mutators', - 'p1073741829': 'stock_mutators', - 's1': false, - 's9': false, - 's11': false, - 's12': false, - 's13': false, - 's14': false, - 'p268435706': false, - 'p268435968': false, - 'p268435969': false - }); - - function split(a) { - var s = a.split('\x1c'); - s = s.filter(function(e) { return e }); - return s; - } - if('custom_mutators' in state.raw) state.raw['custom_mutators'] = split(state.raw['custom_mutators']); - if('stock_mutators' in state.raw) state.raw['stock_mutators'] = split(state.raw['stock_mutators']); - if('map' in state.raw) state.map = state.raw.map; - } -}); +module.exports = require('./gamespy3').extend({ + finalizeState: function(state) { + this._super(state); + + this.translate(state.raw,{ + 'mapname': false, + 'p1073741825': 'map', + 'p1073741826': 'gametype', + 'p1073741827': 'servername', + 'p1073741828': 'custom_mutators', + 'gamemode': 'joininprogress', + 's32779': 'gamemode', + 's0': 'bot_skill', + 's6': 'pure_server', + 's7': 'password', + 's8': 'vs_bots', + 's10': 'force_respawn', + 'p268435704': 'frag_limit', + 'p268435705': 'time_limit', + 'p268435703': 'numbots', + 'p268435717': 'stock_mutators', + 'p1073741829': 'stock_mutators', + 's1': false, + 's9': false, + 's11': false, + 's12': false, + 's13': false, + 's14': false, + 'p268435706': false, + 'p268435968': false, + 'p268435969': false + }); + + function split(a) { + var s = a.split('\x1c'); + s = s.filter(function(e) { return e }); + return s; + } + if('custom_mutators' in state.raw) state.raw['custom_mutators'] = split(state.raw['custom_mutators']); + if('stock_mutators' in state.raw) state.raw['stock_mutators'] = split(state.raw['stock_mutators']); + if('map' in state.raw) state.map = state.raw.map; + } +}); diff --git a/protocols/valve.js b/protocols/valve.js index 5ddbd42..4fe6f65 100644 --- a/protocols/valve.js +++ b/protocols/valve.js @@ -1,325 +1,325 @@ -var async = require('async'), - Bzip2 = require('compressjs').Bzip2; - -module.exports = require('./core').extend({ - init: function() { - this._super(); - - this.options.port = 27015; - - // legacy goldsrc info response -- basically not used by ANYTHING now, - // as most (all?) goldsrc servers respond with the source info reponse - // delete in a few years if nothing ends up using it anymore - this.goldsrcInfo = false; - - // unfortunately, the split format from goldsrc is still around, but we - // can detect that during the query - this.goldsrcSplits = false; - - // some mods require a challenge, but don't provide them in the new format - // at all, use the old dedicated challenge query if needed - this.legacyChallenge = false; - - // cs:go provides an annoying additional bot that looks exactly like a player, - // but is always named "Max Players" - this.isCsGo = false; - - // 2006 engines don't pass packet switching size in split packet header - // while all others do, this need is detected automatically - this._skipSizeInSplitHeader = false; - - this._challenge = ''; - }, - run: function(state) { - var self = this; - async.series([ - function(c) { self.queryInfo(state,c); }, - function(c) { self.queryChallenge(state,c); }, - function(c) { self.queryPlayers(state,c); }, - function(c) { self.queryRules(state,c); }, - function(c) { self.finish(state); } - ]); - }, - queryInfo: function(state,c) { - var self = this; - self.sendPacket( - 0x54,false,'Source Engine Query\0', - self.goldsrcInfo ? 0x6D : 0x49, - function(b) { - var reader = self.reader(b); - - if(self.goldsrcInfo) state.raw.address = reader.string(); - else state.raw.protocol = reader.uint(1); - - state.name = reader.string(); - state.map = reader.string(); - state.raw.folder = reader.string(); - state.raw.game = reader.string(); - state.raw.steamappid = reader.uint(2); - state.raw.numplayers = reader.uint(1); - state.maxplayers = reader.uint(1); - - if(self.goldsrcInfo) state.raw.protocol = reader.uint(1); - else state.raw.numbots = reader.uint(1); - - state.raw.listentype = reader.uint(1); - state.raw.environment = reader.uint(1); - if(!self.goldsrcInfo) { - state.raw.listentype = String.fromCharCode(state.raw.listentype); - state.raw.environment = String.fromCharCode(state.raw.environment); - } - - state.password = !!reader.uint(1); - if(self.goldsrcInfo) { - state.raw.ismod = reader.uint(1); - if(state.raw.ismod) { - state.raw.modlink = reader.string(); - state.raw.moddownload = reader.string(); - reader.skip(1); - state.raw.modversion = reader.uint(4); - state.raw.modsize = reader.uint(4); - state.raw.modtype = reader.uint(1); - state.raw.moddll = reader.uint(1); - } - } - state.raw.secure = reader.uint(1); - - if(self.goldsrcInfo) { - state.raw.numbots = reader.uint(1); - } else { - if(state.raw.folder == 'ship') { - state.raw.shipmode = reader.uint(1); - state.raw.shipwitnesses = reader.uint(1); - state.raw.shipduration = reader.uint(1); - } - state.raw.version = reader.string(); - var extraFlag = reader.uint(1); - if(extraFlag & 0x80) state.raw.port = reader.uint(2); - if(extraFlag & 0x10) state.raw.steamid = reader.uint(8); - if(extraFlag & 0x40) { - state.raw.sourcetvport = reader.uint(2); - state.raw.sourcetvname = reader.string(); - } - if(extraFlag & 0x20) state.raw.tags = reader.string(); - if(extraFlag & 0x01) state.raw.gameid = reader.uint(8); - } - - // from https://developer.valvesoftware.com/wiki/Server_queries - if( - state.raw.protocol == 7 && ( - state.raw.steamappid == 215 - || state.raw.steamappid == 17550 - || state.raw.steamappid == 17700 - || state.raw.steamappid == 240 - ) - ) { - self._skipSizeInSplitHeader = true; - } - if(self.debug) { - console.log("STEAM APPID: "+state.raw.steamappid); - console.log("PROTOCOL: "+state.raw.protocol); - } - if(state.raw.protocol == 48) { - if(self.debug) console.log("GOLDSRC DETECTED - USING MODIFIED SPLIT FORMAT"); - self.goldsrcSplits = true; - } - - c(); - } - ); - }, - queryChallenge: function(state,c) { - var self = this; - if(this.legacyChallenge) { - self.sendPacket(0x57,false,false,0x41,function(b) { - // sendPacket will catch the response packet and - // save the challenge for us - c(); - }); - } else { - c(); - } - }, - queryPlayers: function(state,c) { - var self = this; - self.sendPacket(0x55,true,false,0x44,function(b) { - var reader = self.reader(b); - var num = reader.uint(1); - var csgoHiddenPlayers = false; - for(var i = 0; i < num; i++) { - reader.skip(1); - var name = reader.string(); - var score = reader.int(4); - var time = reader.float(); - - if(self.debug) console.log("Found player: "+name+" "+score+" "+time); - - // connecting players don't count as players. - if(!name) continue; - - (time == -1 ? state.bots : state.players).push({ - name:name, score:score, time:time - }); - } - - if(self.isCsGo && state.players.length == 1 && state.players[0].name == 'Max Players') { - if(self.debug) console.log("CSGO server using limited player details"); - state.players = []; - for(var i = 0; i < state.raw.numplayers; i++) { state.players.push({}); } - } - - // if we didn't find the bots, iterate - // through and guess which ones they are - if(!state.bots.length && state.raw.numbots) { - var maxTime = 0; - state.players.forEach(function(player) { - maxTime = Math.max(player.time,maxTime); - }); - for(var i = 0; i < state.players.length; i++) { - var player = state.players[i]; - if(state.bots.length >= state.raw.numbots) continue; - if(player.time != maxTime) continue; - state.bots.push(player); - state.players.splice(i, 1); - i--; - } - } - - c(); - }); - }, - queryRules: function(state,c) { - var self = this; - self.sendPacket(0x56,true,false,0x45,function(b) { - var reader = self.reader(b); - var num = reader.uint(2); - state.raw.rules = {}; - for(var i = 0; i < num; i++) { - var key = reader.string(); - var value = reader.string(); - state.raw.rules[key] = value; - } - c(); - }, function() { - // no rules were returned after timeout -- - // the server probably has them disabled - // ignore the timeout - c(); - return true; - }); - }, - sendPacket: function(type,sendChallenge,payload,expect,callback,ontimeout) { - var self = this; - var packetStorage = {}; - - send(); - - function send(c) { - if(typeof payload == 'string') payload = new Buffer(payload,'binary'); - var challengeLength = sendChallenge ? 4 : 0; - var payloadLength = payload ? payload.length : 0; - - var b = new Buffer(5 + challengeLength + payloadLength); - b.writeInt32LE(-1, 0); - b.writeUInt8(type, 4); - - if(sendChallenge) { - var challenge = self._challenge; - if(!challenge) challenge = 0xffffffff; - if(self.byteorder == 'le') b.writeUInt32LE(challenge, 5); - else b.writeUInt32BE(challenge, 5); - } - if(payloadLength) payload.copy(b, 5+challengeLength); - - self.udpSend(b,receivedOne,ontimeout); - } - - function receivedOne(buffer) { - var reader = self.reader(buffer); - - var header = reader.int(4); - if(header == -1) { - // full package - if(self.debug) console.log("Received full packet"); - return receivedFull(reader); - } - if(header == -2) { - // partial package - var uid = reader.uint(4); - if(!(uid in packetStorage)) packetStorage[uid] = {}; - var packets = packetStorage[uid]; - - var bzip = false; - if(!self.goldsrcSplits && uid & 0x80000000) bzip = true; - - var packetNum,payload,numPackets; - if(self.goldsrcSplits) { - packetNum = reader.uint(1); - numPackets = packetNum & 0x0f; - packetNum = (packetNum & 0xf0) >> 4; - payload = reader.rest(); - } else { - numPackets = reader.uint(1); - packetNum = reader.uint(1); - if(!self._skipSizeInSplitHeader) reader.skip(2); - if(packetNum == 0 && bzip) reader.skip(8); - payload = reader.rest(); - } - - packets[packetNum] = payload; - - if(self.debug) { - console.log("Received partial packet uid:"+uid+" num:"+packetNum); - console.log("Received "+Object.keys(packets).length+'/'+numPackets+" packets for this UID"); - } - - if(Object.keys(packets).length != numPackets) return; - - // assemble the parts - var list = []; - for(var i = 0; i < numPackets; i++) { - if(!(i in packets)) { - self.fatal('Missing packet #'+i); - return true; - } - list.push(packets[i]); - } - - var assembled = Buffer.concat(list); - if(bzip) { - if(self.debug) console.log("BZIP DETECTED - Extracing packet..."); - try { - assembled = new Buffer(Bzip2.decompressFile(assembled)); - } catch(e) { - self.fatal('Invalid bzip packet'); - return true; - } - } - var assembledReader = self.reader(assembled); - assembledReader.skip(4); // header - return receivedFull(assembledReader); - } - } - - function receivedFull(reader) { - var type = reader.uint(1); - - if(type == 0x41) { - if(self.debug) console.log('Received challenge key'); - if(self._challenge) return self.fatal('Received more than one challenge key'); - self._challenge = reader.uint(4); - - if(sendChallenge) { - if(self.debug) console.log('Restarting query'); - send(); - return true; - } - } - - if(self.debug) console.log("Received "+type.toString(16)+" expected "+expect.toString(16)); - if(type != expect) return; - callback(reader.rest()); - return true; - } - } -}); +var async = require('async'), + Bzip2 = require('compressjs').Bzip2; + +module.exports = require('./core').extend({ + init: function() { + this._super(); + + this.options.port = 27015; + + // legacy goldsrc info response -- basically not used by ANYTHING now, + // as most (all?) goldsrc servers respond with the source info reponse + // delete in a few years if nothing ends up using it anymore + this.goldsrcInfo = false; + + // unfortunately, the split format from goldsrc is still around, but we + // can detect that during the query + this.goldsrcSplits = false; + + // some mods require a challenge, but don't provide them in the new format + // at all, use the old dedicated challenge query if needed + this.legacyChallenge = false; + + // cs:go provides an annoying additional bot that looks exactly like a player, + // but is always named "Max Players" + this.isCsGo = false; + + // 2006 engines don't pass packet switching size in split packet header + // while all others do, this need is detected automatically + this._skipSizeInSplitHeader = false; + + this._challenge = ''; + }, + run: function(state) { + var self = this; + async.series([ + function(c) { self.queryInfo(state,c); }, + function(c) { self.queryChallenge(state,c); }, + function(c) { self.queryPlayers(state,c); }, + function(c) { self.queryRules(state,c); }, + function(c) { self.finish(state); } + ]); + }, + queryInfo: function(state,c) { + var self = this; + self.sendPacket( + 0x54,false,'Source Engine Query\0', + self.goldsrcInfo ? 0x6D : 0x49, + function(b) { + var reader = self.reader(b); + + if(self.goldsrcInfo) state.raw.address = reader.string(); + else state.raw.protocol = reader.uint(1); + + state.name = reader.string(); + state.map = reader.string(); + state.raw.folder = reader.string(); + state.raw.game = reader.string(); + state.raw.steamappid = reader.uint(2); + state.raw.numplayers = reader.uint(1); + state.maxplayers = reader.uint(1); + + if(self.goldsrcInfo) state.raw.protocol = reader.uint(1); + else state.raw.numbots = reader.uint(1); + + state.raw.listentype = reader.uint(1); + state.raw.environment = reader.uint(1); + if(!self.goldsrcInfo) { + state.raw.listentype = String.fromCharCode(state.raw.listentype); + state.raw.environment = String.fromCharCode(state.raw.environment); + } + + state.password = !!reader.uint(1); + if(self.goldsrcInfo) { + state.raw.ismod = reader.uint(1); + if(state.raw.ismod) { + state.raw.modlink = reader.string(); + state.raw.moddownload = reader.string(); + reader.skip(1); + state.raw.modversion = reader.uint(4); + state.raw.modsize = reader.uint(4); + state.raw.modtype = reader.uint(1); + state.raw.moddll = reader.uint(1); + } + } + state.raw.secure = reader.uint(1); + + if(self.goldsrcInfo) { + state.raw.numbots = reader.uint(1); + } else { + if(state.raw.folder == 'ship') { + state.raw.shipmode = reader.uint(1); + state.raw.shipwitnesses = reader.uint(1); + state.raw.shipduration = reader.uint(1); + } + state.raw.version = reader.string(); + var extraFlag = reader.uint(1); + if(extraFlag & 0x80) state.raw.port = reader.uint(2); + if(extraFlag & 0x10) state.raw.steamid = reader.uint(8); + if(extraFlag & 0x40) { + state.raw.sourcetvport = reader.uint(2); + state.raw.sourcetvname = reader.string(); + } + if(extraFlag & 0x20) state.raw.tags = reader.string(); + if(extraFlag & 0x01) state.raw.gameid = reader.uint(8); + } + + // from https://developer.valvesoftware.com/wiki/Server_queries + if( + state.raw.protocol == 7 && ( + state.raw.steamappid == 215 + || state.raw.steamappid == 17550 + || state.raw.steamappid == 17700 + || state.raw.steamappid == 240 + ) + ) { + self._skipSizeInSplitHeader = true; + } + if(self.debug) { + console.log("STEAM APPID: "+state.raw.steamappid); + console.log("PROTOCOL: "+state.raw.protocol); + } + if(state.raw.protocol == 48) { + if(self.debug) console.log("GOLDSRC DETECTED - USING MODIFIED SPLIT FORMAT"); + self.goldsrcSplits = true; + } + + c(); + } + ); + }, + queryChallenge: function(state,c) { + var self = this; + if(this.legacyChallenge) { + self.sendPacket(0x57,false,false,0x41,function(b) { + // sendPacket will catch the response packet and + // save the challenge for us + c(); + }); + } else { + c(); + } + }, + queryPlayers: function(state,c) { + var self = this; + self.sendPacket(0x55,true,false,0x44,function(b) { + var reader = self.reader(b); + var num = reader.uint(1); + var csgoHiddenPlayers = false; + for(var i = 0; i < num; i++) { + reader.skip(1); + var name = reader.string(); + var score = reader.int(4); + var time = reader.float(); + + if(self.debug) console.log("Found player: "+name+" "+score+" "+time); + + // connecting players don't count as players. + if(!name) continue; + + (time == -1 ? state.bots : state.players).push({ + name:name, score:score, time:time + }); + } + + if(self.isCsGo && state.players.length == 1 && state.players[0].name == 'Max Players') { + if(self.debug) console.log("CSGO server using limited player details"); + state.players = []; + for(var i = 0; i < state.raw.numplayers; i++) { state.players.push({}); } + } + + // if we didn't find the bots, iterate + // through and guess which ones they are + if(!state.bots.length && state.raw.numbots) { + var maxTime = 0; + state.players.forEach(function(player) { + maxTime = Math.max(player.time,maxTime); + }); + for(var i = 0; i < state.players.length; i++) { + var player = state.players[i]; + if(state.bots.length >= state.raw.numbots) continue; + if(player.time != maxTime) continue; + state.bots.push(player); + state.players.splice(i, 1); + i--; + } + } + + c(); + }); + }, + queryRules: function(state,c) { + var self = this; + self.sendPacket(0x56,true,false,0x45,function(b) { + var reader = self.reader(b); + var num = reader.uint(2); + state.raw.rules = {}; + for(var i = 0; i < num; i++) { + var key = reader.string(); + var value = reader.string(); + state.raw.rules[key] = value; + } + c(); + }, function() { + // no rules were returned after timeout -- + // the server probably has them disabled + // ignore the timeout + c(); + return true; + }); + }, + sendPacket: function(type,sendChallenge,payload,expect,callback,ontimeout) { + var self = this; + var packetStorage = {}; + + send(); + + function send(c) { + if(typeof payload == 'string') payload = new Buffer(payload,'binary'); + var challengeLength = sendChallenge ? 4 : 0; + var payloadLength = payload ? payload.length : 0; + + var b = new Buffer(5 + challengeLength + payloadLength); + b.writeInt32LE(-1, 0); + b.writeUInt8(type, 4); + + if(sendChallenge) { + var challenge = self._challenge; + if(!challenge) challenge = 0xffffffff; + if(self.byteorder == 'le') b.writeUInt32LE(challenge, 5); + else b.writeUInt32BE(challenge, 5); + } + if(payloadLength) payload.copy(b, 5+challengeLength); + + self.udpSend(b,receivedOne,ontimeout); + } + + function receivedOne(buffer) { + var reader = self.reader(buffer); + + var header = reader.int(4); + if(header == -1) { + // full package + if(self.debug) console.log("Received full packet"); + return receivedFull(reader); + } + if(header == -2) { + // partial package + var uid = reader.uint(4); + if(!(uid in packetStorage)) packetStorage[uid] = {}; + var packets = packetStorage[uid]; + + var bzip = false; + if(!self.goldsrcSplits && uid & 0x80000000) bzip = true; + + var packetNum,payload,numPackets; + if(self.goldsrcSplits) { + packetNum = reader.uint(1); + numPackets = packetNum & 0x0f; + packetNum = (packetNum & 0xf0) >> 4; + payload = reader.rest(); + } else { + numPackets = reader.uint(1); + packetNum = reader.uint(1); + if(!self._skipSizeInSplitHeader) reader.skip(2); + if(packetNum == 0 && bzip) reader.skip(8); + payload = reader.rest(); + } + + packets[packetNum] = payload; + + if(self.debug) { + console.log("Received partial packet uid:"+uid+" num:"+packetNum); + console.log("Received "+Object.keys(packets).length+'/'+numPackets+" packets for this UID"); + } + + if(Object.keys(packets).length != numPackets) return; + + // assemble the parts + var list = []; + for(var i = 0; i < numPackets; i++) { + if(!(i in packets)) { + self.fatal('Missing packet #'+i); + return true; + } + list.push(packets[i]); + } + + var assembled = Buffer.concat(list); + if(bzip) { + if(self.debug) console.log("BZIP DETECTED - Extracing packet..."); + try { + assembled = new Buffer(Bzip2.decompressFile(assembled)); + } catch(e) { + self.fatal('Invalid bzip packet'); + return true; + } + } + var assembledReader = self.reader(assembled); + assembledReader.skip(4); // header + return receivedFull(assembledReader); + } + } + + function receivedFull(reader) { + var type = reader.uint(1); + + if(type == 0x41) { + if(self.debug) console.log('Received challenge key'); + if(self._challenge) return self.fatal('Received more than one challenge key'); + self._challenge = reader.uint(4); + + if(sendChallenge) { + if(self.debug) console.log('Restarting query'); + send(); + return true; + } + } + + if(self.debug) console.log("Received "+type.toString(16)+" expected "+expect.toString(16)); + if(type != expect) return; + callback(reader.rest()); + return true; + } + } +}); diff --git a/protocols/ventrilo.js b/protocols/ventrilo.js index 1ad53f0..101924b 100644 --- a/protocols/ventrilo.js +++ b/protocols/ventrilo.js @@ -1,240 +1,240 @@ -var async = require('async'); - -module.exports = require('./core').extend({ - init: function() { - this._super(); - this.byteorder = 'be'; - }, - run: function(state) { - var self = this; - - this.sendCommand(2,'',function(data) { - state.raw = splitFields(data.toString()); - state.raw.CLIENTS.forEach(function(client) { - client.name = client.NAME; - delete client.NAME; - client.ping = parseInt(client.PING); - delete client.PING; - state.players.push(client); - }); - delete state.raw.CLIENTS; - - if('NAME' in state.raw) state.name = state.raw.NAME; - if('MAXCLIENTS' in state.raw) state.maxplayers = state.raw.MAXCLIENTS; - if(self.trueTest(state.raw.AUTH)) state.password = true; - self.finish(state); - }); - }, - sendCommand: function(cmd,password,c) { - var self = this; - var body = new Buffer(16); - body.write(password,0,15,'utf8'); - var encrypted = encrypt(cmd,body); - - var packets = {}; - this.udpSend(encrypted, function(buffer) { - if(buffer.length < 20) return; - var data = decrypt(buffer); - - if(data.zero != 0) return; - packets[data.packetNum] = data.body; - if(Object.keys(packets).length != data.packetTotal) return; - - var out = []; - for(var i = 0; i < data.packetTotal; i++) { - if(!(i in packets)) return self.fatal('Missing packet #'+i); - out.push(packets[i]); - } - c(Buffer.concat(out)); - return true; - }); - } -}); - -function splitFields(str,subMode) { - var splitter,delim; - if(subMode) { - splitter = '='; - delim = ','; - } else { - splitter = ': '; - delim = '\n'; - } - - var split = str.split(delim); - var out = {}; - if(!subMode) { - out.CHANNELS = []; - out.CLIENTS = []; - } - split.forEach(function(one) { - var equal = one.indexOf(splitter); - var key = equal == -1 ? one : one.substr(0,equal); - if(!key || key == '\0') return; - var value = equal == -1 ? '' : one.substr(equal+splitter.length); - if(!subMode && key == 'CHANNEL') out.CHANNELS.push(splitFields(value,true)); - else if(!subMode && key == 'CLIENT') out.CLIENTS.push(splitFields(value,true)); - else out[key] = value; - }); - return out; -} - -function randInt(min,max) { - return Math.floor(Math.random()*(max-min+1)+min) -} - -function crc(body) { - var crc = 0; - for(var i = 0; i < body.length; i++) { - crc = crc_table[crc>>8] ^ body.readUInt8(i) ^ (crc<<8); - crc &= 0xffff; - } - return crc; -} - -function encrypt(cmd,body) { - var headerKeyStart = randInt(0,0xff); - var headerKeyAdd = randInt(1,0xff); - var bodyKeyStart = randInt(0,0xff); - var bodyKeyAdd = randInt(1,0xff); - - var header = new Buffer(20); - header.writeUInt8(headerKeyStart,0); - header.writeUInt8(headerKeyAdd,1); - header.writeUInt16BE(cmd,4); - header.writeUInt16BE(body.length,8); - header.writeUInt16BE(body.length,10); - header.writeUInt16BE(1,12); - header.writeUInt16BE(0,14); - header.writeUInt8(bodyKeyStart,16); - header.writeUInt8(bodyKeyAdd,17); - header.writeUInt16BE(crc(body),18); - - var offset = headerKeyStart; - for(var i = 2; i < header.length; i++) { - var val = header.readUInt8(i); - val += code_head.charCodeAt(offset) + ((i-2) % 5); - val = val & 0xff; - header.writeUInt8(val,i); - offset = (offset+headerKeyAdd) & 0xff; - } - - offset = bodyKeyStart; - for(var i = 0; i < body.length; i++) { - var val = body.readUInt8(i); - val += code_body.charCodeAt(offset) + (i % 72); - val = val & 0xff; - body.writeUInt8(val,i); - offset = (offset+bodyKeyAdd) & 0xff; - } - - return Buffer.concat([header,body]); -} -function decrypt(data) { - var header = data.slice(0,20); - var body = data.slice(20); - var headerKeyStart = header.readUInt8(0); - var headerKeyAdd = header.readUInt8(1); - - var offset = headerKeyStart; - for(var i = 2; i < header.length; i++) { - var val = header.readUInt8(i); - val -= code_head.charCodeAt(offset) + ((i-2) % 5); - val = val & 0xff; - header.writeUInt8(val,i); - offset = (offset+headerKeyAdd) & 0xff; - } - - var bodyKeyStart = header.readUInt8(16); - var bodyKeyAdd = header.readUInt8(17); - offset = bodyKeyStart; - for(var i = 0; i < body.length; i++) { - var val = body.readUInt8(i); - val -= code_body.charCodeAt(offset) + (i % 72); - val = val & 0xff; - body.writeUInt8(val,i); - offset = (offset+bodyKeyAdd) & 0xff; - } - - // header format: - // key, zero, cmd, echo, totallength, thislength - // totalpacket, packetnum, body key, crc - return { - zero: header.readUInt16BE(2), - cmd: header.readUInt16BE(4), - packetTotal: header.readUInt16BE(12), - packetNum: header.readUInt16BE(14), - body: body - }; -} - -var code_head = - '\x80\xe5\x0e\x38\xba\x63\x4c\x99\x88\x63\x4c\xd6\x54\xb8\x65\x7e'+ - '\xbf\x8a\xf0\x17\x8a\xaa\x4d\x0f\xb7\x23\x27\xf6\xeb\x12\xf8\xea'+ - '\x17\xb7\xcf\x52\x57\xcb\x51\xcf\x1b\x14\xfd\x6f\x84\x38\xb5\x24'+ - '\x11\xcf\x7a\x75\x7a\xbb\x78\x74\xdc\xbc\x42\xf0\x17\x3f\x5e\xeb'+ - '\x74\x77\x04\x4e\x8c\xaf\x23\xdc\x65\xdf\xa5\x65\xdd\x7d\xf4\x3c'+ - '\x4c\x95\xbd\xeb\x65\x1c\xf4\x24\x5d\x82\x18\xfb\x50\x86\xb8\x53'+ - '\xe0\x4e\x36\x96\x1f\xb7\xcb\xaa\xaf\xea\xcb\x20\x27\x30\x2a\xae'+ - '\xb9\x07\x40\xdf\x12\x75\xc9\x09\x82\x9c\x30\x80\x5d\x8f\x0d\x09'+ - '\xa1\x64\xec\x91\xd8\x8a\x50\x1f\x40\x5d\xf7\x08\x2a\xf8\x60\x62'+ - '\xa0\x4a\x8b\xba\x4a\x6d\x00\x0a\x93\x32\x12\xe5\x07\x01\x65\xf5'+ - '\xff\xe0\xae\xa7\x81\xd1\xba\x25\x62\x61\xb2\x85\xad\x7e\x9d\x3f'+ - '\x49\x89\x26\xe5\xd5\xac\x9f\x0e\xd7\x6e\x47\x94\x16\x84\xc8\xff'+ - '\x44\xea\x04\x40\xe0\x33\x11\xa3\x5b\x1e\x82\xff\x7a\x69\xe9\x2f'+ - '\xfb\xea\x9a\xc6\x7b\xdb\xb1\xff\x97\x76\x56\xf3\x52\xc2\x3f\x0f'+ - '\xb6\xac\x77\xc4\xbf\x59\x5e\x80\x74\xbb\xf2\xde\x57\x62\x4c\x1a'+ - '\xff\x95\x6d\xc7\x04\xa2\x3b\xc4\x1b\x72\xc7\x6c\x82\x60\xd1\x0d'; - -var code_body = - '\x82\x8b\x7f\x68\x90\xe0\x44\x09\x19\x3b\x8e\x5f\xc2\x82\x38\x23'+ - '\x6d\xdb\x62\x49\x52\x6e\x21\xdf\x51\x6c\x76\x37\x86\x50\x7d\x48'+ - '\x1f\x65\xe7\x52\x6a\x88\xaa\xc1\x32\x2f\xf7\x54\x4c\xaa\x6d\x7e'+ - '\x6d\xa9\x8c\x0d\x3f\xff\x6c\x09\xb3\xa5\xaf\xdf\x98\x02\xb4\xbe'+ - '\x6d\x69\x0d\x42\x73\xe4\x34\x50\x07\x30\x79\x41\x2f\x08\x3f\x42'+ - '\x73\xa7\x68\xfa\xee\x88\x0e\x6e\xa4\x70\x74\x22\x16\xae\x3c\x81'+ - '\x14\xa1\xda\x7f\xd3\x7c\x48\x7d\x3f\x46\xfb\x6d\x92\x25\x17\x36'+ - '\x26\xdb\xdf\x5a\x87\x91\x6f\xd6\xcd\xd4\xad\x4a\x29\xdd\x7d\x59'+ - '\xbd\x15\x34\x53\xb1\xd8\x50\x11\x83\x79\x66\x21\x9e\x87\x5b\x24'+ - '\x2f\x4f\xd7\x73\x34\xa2\xf7\x09\xd5\xd9\x42\x9d\xf8\x15\xdf\x0e'+ - '\x10\xcc\x05\x04\x35\x81\xb2\xd5\x7a\xd2\xa0\xa5\x7b\xb8\x75\xd2'+ - '\x35\x0b\x39\x8f\x1b\x44\x0e\xce\x66\x87\x1b\x64\xac\xe1\xca\x67'+ - '\xb4\xce\x33\xdb\x89\xfe\xd8\x8e\xcd\x58\x92\x41\x50\x40\xcb\x08'+ - '\xe1\x15\xee\xf4\x64\xfe\x1c\xee\x25\xe7\x21\xe6\x6c\xc6\xa6\x2e'+ - '\x52\x23\xa7\x20\xd2\xd7\x28\x07\x23\x14\x24\x3d\x45\xa5\xc7\x90'+ - '\xdb\x77\xdd\xea\x38\x59\x89\x32\xbc\x00\x3a\x6d\x61\x4e\xdb\x29'; - -var crc_table = [ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -]; +var async = require('async'); + +module.exports = require('./core').extend({ + init: function() { + this._super(); + this.byteorder = 'be'; + }, + run: function(state) { + var self = this; + + this.sendCommand(2,'',function(data) { + state.raw = splitFields(data.toString()); + state.raw.CLIENTS.forEach(function(client) { + client.name = client.NAME; + delete client.NAME; + client.ping = parseInt(client.PING); + delete client.PING; + state.players.push(client); + }); + delete state.raw.CLIENTS; + + if('NAME' in state.raw) state.name = state.raw.NAME; + if('MAXCLIENTS' in state.raw) state.maxplayers = state.raw.MAXCLIENTS; + if(self.trueTest(state.raw.AUTH)) state.password = true; + self.finish(state); + }); + }, + sendCommand: function(cmd,password,c) { + var self = this; + var body = new Buffer(16); + body.write(password,0,15,'utf8'); + var encrypted = encrypt(cmd,body); + + var packets = {}; + this.udpSend(encrypted, function(buffer) { + if(buffer.length < 20) return; + var data = decrypt(buffer); + + if(data.zero != 0) return; + packets[data.packetNum] = data.body; + if(Object.keys(packets).length != data.packetTotal) return; + + var out = []; + for(var i = 0; i < data.packetTotal; i++) { + if(!(i in packets)) return self.fatal('Missing packet #'+i); + out.push(packets[i]); + } + c(Buffer.concat(out)); + return true; + }); + } +}); + +function splitFields(str,subMode) { + var splitter,delim; + if(subMode) { + splitter = '='; + delim = ','; + } else { + splitter = ': '; + delim = '\n'; + } + + var split = str.split(delim); + var out = {}; + if(!subMode) { + out.CHANNELS = []; + out.CLIENTS = []; + } + split.forEach(function(one) { + var equal = one.indexOf(splitter); + var key = equal == -1 ? one : one.substr(0,equal); + if(!key || key == '\0') return; + var value = equal == -1 ? '' : one.substr(equal+splitter.length); + if(!subMode && key == 'CHANNEL') out.CHANNELS.push(splitFields(value,true)); + else if(!subMode && key == 'CLIENT') out.CLIENTS.push(splitFields(value,true)); + else out[key] = value; + }); + return out; +} + +function randInt(min,max) { + return Math.floor(Math.random()*(max-min+1)+min) +} + +function crc(body) { + var crc = 0; + for(var i = 0; i < body.length; i++) { + crc = crc_table[crc>>8] ^ body.readUInt8(i) ^ (crc<<8); + crc &= 0xffff; + } + return crc; +} + +function encrypt(cmd,body) { + var headerKeyStart = randInt(0,0xff); + var headerKeyAdd = randInt(1,0xff); + var bodyKeyStart = randInt(0,0xff); + var bodyKeyAdd = randInt(1,0xff); + + var header = new Buffer(20); + header.writeUInt8(headerKeyStart,0); + header.writeUInt8(headerKeyAdd,1); + header.writeUInt16BE(cmd,4); + header.writeUInt16BE(body.length,8); + header.writeUInt16BE(body.length,10); + header.writeUInt16BE(1,12); + header.writeUInt16BE(0,14); + header.writeUInt8(bodyKeyStart,16); + header.writeUInt8(bodyKeyAdd,17); + header.writeUInt16BE(crc(body),18); + + var offset = headerKeyStart; + for(var i = 2; i < header.length; i++) { + var val = header.readUInt8(i); + val += code_head.charCodeAt(offset) + ((i-2) % 5); + val = val & 0xff; + header.writeUInt8(val,i); + offset = (offset+headerKeyAdd) & 0xff; + } + + offset = bodyKeyStart; + for(var i = 0; i < body.length; i++) { + var val = body.readUInt8(i); + val += code_body.charCodeAt(offset) + (i % 72); + val = val & 0xff; + body.writeUInt8(val,i); + offset = (offset+bodyKeyAdd) & 0xff; + } + + return Buffer.concat([header,body]); +} +function decrypt(data) { + var header = data.slice(0,20); + var body = data.slice(20); + var headerKeyStart = header.readUInt8(0); + var headerKeyAdd = header.readUInt8(1); + + var offset = headerKeyStart; + for(var i = 2; i < header.length; i++) { + var val = header.readUInt8(i); + val -= code_head.charCodeAt(offset) + ((i-2) % 5); + val = val & 0xff; + header.writeUInt8(val,i); + offset = (offset+headerKeyAdd) & 0xff; + } + + var bodyKeyStart = header.readUInt8(16); + var bodyKeyAdd = header.readUInt8(17); + offset = bodyKeyStart; + for(var i = 0; i < body.length; i++) { + var val = body.readUInt8(i); + val -= code_body.charCodeAt(offset) + (i % 72); + val = val & 0xff; + body.writeUInt8(val,i); + offset = (offset+bodyKeyAdd) & 0xff; + } + + // header format: + // key, zero, cmd, echo, totallength, thislength + // totalpacket, packetnum, body key, crc + return { + zero: header.readUInt16BE(2), + cmd: header.readUInt16BE(4), + packetTotal: header.readUInt16BE(12), + packetNum: header.readUInt16BE(14), + body: body + }; +} + +var code_head = + '\x80\xe5\x0e\x38\xba\x63\x4c\x99\x88\x63\x4c\xd6\x54\xb8\x65\x7e'+ + '\xbf\x8a\xf0\x17\x8a\xaa\x4d\x0f\xb7\x23\x27\xf6\xeb\x12\xf8\xea'+ + '\x17\xb7\xcf\x52\x57\xcb\x51\xcf\x1b\x14\xfd\x6f\x84\x38\xb5\x24'+ + '\x11\xcf\x7a\x75\x7a\xbb\x78\x74\xdc\xbc\x42\xf0\x17\x3f\x5e\xeb'+ + '\x74\x77\x04\x4e\x8c\xaf\x23\xdc\x65\xdf\xa5\x65\xdd\x7d\xf4\x3c'+ + '\x4c\x95\xbd\xeb\x65\x1c\xf4\x24\x5d\x82\x18\xfb\x50\x86\xb8\x53'+ + '\xe0\x4e\x36\x96\x1f\xb7\xcb\xaa\xaf\xea\xcb\x20\x27\x30\x2a\xae'+ + '\xb9\x07\x40\xdf\x12\x75\xc9\x09\x82\x9c\x30\x80\x5d\x8f\x0d\x09'+ + '\xa1\x64\xec\x91\xd8\x8a\x50\x1f\x40\x5d\xf7\x08\x2a\xf8\x60\x62'+ + '\xa0\x4a\x8b\xba\x4a\x6d\x00\x0a\x93\x32\x12\xe5\x07\x01\x65\xf5'+ + '\xff\xe0\xae\xa7\x81\xd1\xba\x25\x62\x61\xb2\x85\xad\x7e\x9d\x3f'+ + '\x49\x89\x26\xe5\xd5\xac\x9f\x0e\xd7\x6e\x47\x94\x16\x84\xc8\xff'+ + '\x44\xea\x04\x40\xe0\x33\x11\xa3\x5b\x1e\x82\xff\x7a\x69\xe9\x2f'+ + '\xfb\xea\x9a\xc6\x7b\xdb\xb1\xff\x97\x76\x56\xf3\x52\xc2\x3f\x0f'+ + '\xb6\xac\x77\xc4\xbf\x59\x5e\x80\x74\xbb\xf2\xde\x57\x62\x4c\x1a'+ + '\xff\x95\x6d\xc7\x04\xa2\x3b\xc4\x1b\x72\xc7\x6c\x82\x60\xd1\x0d'; + +var code_body = + '\x82\x8b\x7f\x68\x90\xe0\x44\x09\x19\x3b\x8e\x5f\xc2\x82\x38\x23'+ + '\x6d\xdb\x62\x49\x52\x6e\x21\xdf\x51\x6c\x76\x37\x86\x50\x7d\x48'+ + '\x1f\x65\xe7\x52\x6a\x88\xaa\xc1\x32\x2f\xf7\x54\x4c\xaa\x6d\x7e'+ + '\x6d\xa9\x8c\x0d\x3f\xff\x6c\x09\xb3\xa5\xaf\xdf\x98\x02\xb4\xbe'+ + '\x6d\x69\x0d\x42\x73\xe4\x34\x50\x07\x30\x79\x41\x2f\x08\x3f\x42'+ + '\x73\xa7\x68\xfa\xee\x88\x0e\x6e\xa4\x70\x74\x22\x16\xae\x3c\x81'+ + '\x14\xa1\xda\x7f\xd3\x7c\x48\x7d\x3f\x46\xfb\x6d\x92\x25\x17\x36'+ + '\x26\xdb\xdf\x5a\x87\x91\x6f\xd6\xcd\xd4\xad\x4a\x29\xdd\x7d\x59'+ + '\xbd\x15\x34\x53\xb1\xd8\x50\x11\x83\x79\x66\x21\x9e\x87\x5b\x24'+ + '\x2f\x4f\xd7\x73\x34\xa2\xf7\x09\xd5\xd9\x42\x9d\xf8\x15\xdf\x0e'+ + '\x10\xcc\x05\x04\x35\x81\xb2\xd5\x7a\xd2\xa0\xa5\x7b\xb8\x75\xd2'+ + '\x35\x0b\x39\x8f\x1b\x44\x0e\xce\x66\x87\x1b\x64\xac\xe1\xca\x67'+ + '\xb4\xce\x33\xdb\x89\xfe\xd8\x8e\xcd\x58\x92\x41\x50\x40\xcb\x08'+ + '\xe1\x15\xee\xf4\x64\xfe\x1c\xee\x25\xe7\x21\xe6\x6c\xc6\xa6\x2e'+ + '\x52\x23\xa7\x20\xd2\xd7\x28\x07\x23\x14\x24\x3d\x45\xa5\xc7\x90'+ + '\xdb\x77\xdd\xea\x38\x59\x89\x32\xbc\x00\x3a\x6d\x61\x4e\xdb\x29'; + +var crc_table = [ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +]; diff --git a/protocols/warsow.js b/protocols/warsow.js index 1b9856f..9f43ca2 100644 --- a/protocols/warsow.js +++ b/protocols/warsow.js @@ -1,12 +1,12 @@ -module.exports = require('./quake3').extend({ - finalizeState: function(state) { - this._super(state); - if(state.players) { - for(var i = 0; i < state.players.length; i++) { - var player = state.players[i]; - player.team = player.address; - delete player.address; - } - } - } -}); +module.exports = require('./quake3').extend({ + finalizeState: function(state) { + this._super(state); + if(state.players) { + for(var i = 0; i < state.players.length; i++) { + var player = state.players[i]; + player.team = player.address; + delete player.address; + } + } + } +}); diff --git a/reference/LICENSE b/reference/LICENSE index fc6c148..7a77218 100644 --- a/reference/LICENSE +++ b/reference/LICENSE @@ -1,5 +1,5 @@ -The files in this folder are INDIVIDUALLY LICENSED. -The applicable license is located in the header of each individual file. -These files are not covered by the node-GameDig project license. -The source of node-GameDig does not use or execute the content of -any file of this folder, and maintains a separate, unrelated license. +The files in this folder are INDIVIDUALLY LICENSED. +The applicable license is located in the header of each individual file. +These files are not covered by the node-GameDig project license. +The source of node-GameDig does not use or execute the content of +any file of this folder, and maintains a separate, unrelated license. diff --git a/reference/bfris/gamedignote.txt b/reference/bfris/gamedignote.txt index 6791d04..2da5961 100644 --- a/reference/bfris/gamedignote.txt +++ b/reference/bfris/gamedignote.txt @@ -1,2 +1,2 @@ -gamedig note: -connect over TCP, and the data just starts coming, no packet needs sent? +gamedig note: +connect over TCP, and the data just starts coming, no packet needs sent? diff --git a/reference/bfris/qstat.txt b/reference/bfris/qstat.txt index bce1b72..ee275b6 100644 --- a/reference/bfris/qstat.txt +++ b/reference/bfris/qstat.txt @@ -1,276 +1,276 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - -{ - /* BFRIS */ - BFRIS_SERVER, /* id */ - "BFS", /* type_prefix */ - "bfs", /* type_string */ - "-bfs", /* type_option */ - "BFRIS", /* game_name */ - 0, /* master */ - BFRIS_DEFAULT_PORT, /* default_port */ - 0, /* port_offset */ - TF_TCP_CONNECT, /* flags */ - "Rules", /* game_rule */ - "BFRIS", /* template_var */ - NULL, /* status_packet */ - 0, /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_bfris_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_bfris_player_info,/* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_bfris_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_bfris_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_bfris_packet, /* packet_func */ -}, - - - - -/* postions of map name, player name (in player substring), zero-based */ -#define BFRIS_MAP_POS 18 -#define BFRIS_PNAME_POS 11 -query_status_t deal_with_bfris_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - int i, player_data_pos, nplayers; - SavedData *sdata; - unsigned char *saved_data; - int saved_data_size; - - debug( 2, "deal_with_bfris_packet %p, %d", server, pktlen ); - - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - - /* add to the data previously saved */ - sdata = &server->saved_data; - if (!sdata->data) - { - sdata->data = (char*)malloc(pktlen); - } - else - { - sdata->data = (char*)realloc(sdata->data, sdata->datalen + pktlen); - } - - memcpy(sdata->data + sdata->datalen, rawpkt, pktlen); - sdata->datalen += pktlen; - - saved_data = (unsigned char*)sdata->data; - saved_data_size = sdata->datalen; - - /* after we get the server portion of the data, server->game != NULL */ - if (!server->game) - { - - /* server data goes up to map name */ - if (sdata->datalen <= BFRIS_MAP_POS) - { - return INPROGRESS; - } - - /* see if map name is complete */ - player_data_pos = 0; - for (i = BFRIS_MAP_POS; i < saved_data_size; i++) - { - if (saved_data[i] == '\0') - { - player_data_pos = i + 1; - /* data must extend beyond map name */ - if (saved_data_size <= player_data_pos) - { - return INPROGRESS; - } - break; - } - } - - /* did we find beginning of player data? */ - if (!player_data_pos) - { - return INPROGRESS; - } - - /* now we can go ahead and fill in server data */ - server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS); - server->max_players = saved_data[12]; - server->protocol_version = saved_data[11]; - - /* save game type */ - switch (saved_data[13] &15) - { - case 0: - server->game = "FFA"; - break; - case 5: - server->game = "Rover"; - break; - case 6: - server->game = "Occupation"; - break; - case 7: - server->game = "SPAAL"; - break; - case 8: - server->game = "CTF"; - break; - default: - server->game = "unknown"; - break; - } - server->flags |= FLAG_DO_NOT_FREE_GAME; - add_rule(server, server->type->game_rule, server->game, NO_FLAGS); - - if (get_server_rules) - { - char buf[24]; - - /* server revision */ - sprintf(buf, "%d", (unsigned int)saved_data[11]); - add_rule(server, "Revision", buf, NO_FLAGS); - - /* latency */ - sprintf(buf, "%d", (unsigned int)saved_data[10]); - add_rule(server, "Latency", buf, NO_FLAGS); - - /* player allocation */ - add_rule(server, "Allocation", saved_data[13] &16 ? "Automatic" : "Manual", NO_FLAGS); - - } - - } - - /* If we got this far, we know the data saved goes at least to the start of - the player information, and that the server data is taken care of. - */ - - /* start of player data */ - player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1; - - /* ensure all player data have arrived */ - nplayers = 0; - while (saved_data[player_data_pos] != '\0') - { - - player_data_pos += BFRIS_PNAME_POS; - - /* does player data extend to player name? */ - if (saved_data_size <= player_data_pos + 1) - { - return INPROGRESS; - } - - /* does player data extend to end of player name? */ - for (i = 0; player_data_pos + i < saved_data_size; i++) - { - - if (saved_data_size == player_data_pos + i + 1) - { - return INPROGRESS; - } - - if (saved_data[player_data_pos + i] == '\0') - { - player_data_pos += i + 1; - nplayers++; - break; - } - } - } - /* all player data are complete */ - - server->num_players = nplayers; - - if (get_player_info) - { - - /* start of player data */ - player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1; - - for (i = 0; i < nplayers; i++) - { - struct player *player; - player = add_player(server, saved_data[player_data_pos]); - - player->ship = saved_data[player_data_pos + 1]; - player->ping = saved_data[player_data_pos + 2]; - player->frags = saved_data[player_data_pos + 3]; - player->team = saved_data[player_data_pos + 4]; - switch (player->team) - { - case 0: - player->team_name = "silver"; - break; - case 1: - player->team_name = "red"; - break; - case 2: - player->team_name = "blue"; - break; - case 3: - player->team_name = "green"; - break; - case 4: - player->team_name = "purple"; - break; - case 5: - player->team_name = "yellow"; - break; - case 6: - player->team_name = "cyan"; - break; - default: - player->team_name = "unknown"; - break; - } - player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; - player->room = saved_data[player_data_pos + 5]; - - /* score is little-endian integer */ - player->score = saved_data[player_data_pos + 7] + - (saved_data[player_data_pos + 8] << 8) + - (saved_data[player_data_pos + 9] << 16) + - (saved_data[player_data_pos + 10] << 24); - - /* for archs with > 4-byte int */ - if (player->score &0x80000000) - { - player->score = - (~(player->score)) - 1; - } - - - player_data_pos += BFRIS_PNAME_POS; - player->name = strdup((char*)saved_data + player_data_pos); - - player_data_pos += strlen(player->name) + 1; - } - - } - - server->server_name = BFRIS_SERVER_NAME; - - return DONE_FORCE; -} - - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + +{ + /* BFRIS */ + BFRIS_SERVER, /* id */ + "BFS", /* type_prefix */ + "bfs", /* type_string */ + "-bfs", /* type_option */ + "BFRIS", /* game_name */ + 0, /* master */ + BFRIS_DEFAULT_PORT, /* default_port */ + 0, /* port_offset */ + TF_TCP_CONNECT, /* flags */ + "Rules", /* game_rule */ + "BFRIS", /* template_var */ + NULL, /* status_packet */ + 0, /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_bfris_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_bfris_player_info,/* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_bfris_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_bfris_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_bfris_packet, /* packet_func */ +}, + + + + +/* postions of map name, player name (in player substring), zero-based */ +#define BFRIS_MAP_POS 18 +#define BFRIS_PNAME_POS 11 +query_status_t deal_with_bfris_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + int i, player_data_pos, nplayers; + SavedData *sdata; + unsigned char *saved_data; + int saved_data_size; + + debug( 2, "deal_with_bfris_packet %p, %d", server, pktlen ); + + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + + /* add to the data previously saved */ + sdata = &server->saved_data; + if (!sdata->data) + { + sdata->data = (char*)malloc(pktlen); + } + else + { + sdata->data = (char*)realloc(sdata->data, sdata->datalen + pktlen); + } + + memcpy(sdata->data + sdata->datalen, rawpkt, pktlen); + sdata->datalen += pktlen; + + saved_data = (unsigned char*)sdata->data; + saved_data_size = sdata->datalen; + + /* after we get the server portion of the data, server->game != NULL */ + if (!server->game) + { + + /* server data goes up to map name */ + if (sdata->datalen <= BFRIS_MAP_POS) + { + return INPROGRESS; + } + + /* see if map name is complete */ + player_data_pos = 0; + for (i = BFRIS_MAP_POS; i < saved_data_size; i++) + { + if (saved_data[i] == '\0') + { + player_data_pos = i + 1; + /* data must extend beyond map name */ + if (saved_data_size <= player_data_pos) + { + return INPROGRESS; + } + break; + } + } + + /* did we find beginning of player data? */ + if (!player_data_pos) + { + return INPROGRESS; + } + + /* now we can go ahead and fill in server data */ + server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS); + server->max_players = saved_data[12]; + server->protocol_version = saved_data[11]; + + /* save game type */ + switch (saved_data[13] &15) + { + case 0: + server->game = "FFA"; + break; + case 5: + server->game = "Rover"; + break; + case 6: + server->game = "Occupation"; + break; + case 7: + server->game = "SPAAL"; + break; + case 8: + server->game = "CTF"; + break; + default: + server->game = "unknown"; + break; + } + server->flags |= FLAG_DO_NOT_FREE_GAME; + add_rule(server, server->type->game_rule, server->game, NO_FLAGS); + + if (get_server_rules) + { + char buf[24]; + + /* server revision */ + sprintf(buf, "%d", (unsigned int)saved_data[11]); + add_rule(server, "Revision", buf, NO_FLAGS); + + /* latency */ + sprintf(buf, "%d", (unsigned int)saved_data[10]); + add_rule(server, "Latency", buf, NO_FLAGS); + + /* player allocation */ + add_rule(server, "Allocation", saved_data[13] &16 ? "Automatic" : "Manual", NO_FLAGS); + + } + + } + + /* If we got this far, we know the data saved goes at least to the start of + the player information, and that the server data is taken care of. + */ + + /* start of player data */ + player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1; + + /* ensure all player data have arrived */ + nplayers = 0; + while (saved_data[player_data_pos] != '\0') + { + + player_data_pos += BFRIS_PNAME_POS; + + /* does player data extend to player name? */ + if (saved_data_size <= player_data_pos + 1) + { + return INPROGRESS; + } + + /* does player data extend to end of player name? */ + for (i = 0; player_data_pos + i < saved_data_size; i++) + { + + if (saved_data_size == player_data_pos + i + 1) + { + return INPROGRESS; + } + + if (saved_data[player_data_pos + i] == '\0') + { + player_data_pos += i + 1; + nplayers++; + break; + } + } + } + /* all player data are complete */ + + server->num_players = nplayers; + + if (get_player_info) + { + + /* start of player data */ + player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1; + + for (i = 0; i < nplayers; i++) + { + struct player *player; + player = add_player(server, saved_data[player_data_pos]); + + player->ship = saved_data[player_data_pos + 1]; + player->ping = saved_data[player_data_pos + 2]; + player->frags = saved_data[player_data_pos + 3]; + player->team = saved_data[player_data_pos + 4]; + switch (player->team) + { + case 0: + player->team_name = "silver"; + break; + case 1: + player->team_name = "red"; + break; + case 2: + player->team_name = "blue"; + break; + case 3: + player->team_name = "green"; + break; + case 4: + player->team_name = "purple"; + break; + case 5: + player->team_name = "yellow"; + break; + case 6: + player->team_name = "cyan"; + break; + default: + player->team_name = "unknown"; + break; + } + player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; + player->room = saved_data[player_data_pos + 5]; + + /* score is little-endian integer */ + player->score = saved_data[player_data_pos + 7] + + (saved_data[player_data_pos + 8] << 8) + + (saved_data[player_data_pos + 9] << 16) + + (saved_data[player_data_pos + 10] << 24); + + /* for archs with > 4-byte int */ + if (player->score &0x80000000) + { + player->score = - (~(player->score)) - 1; + } + + + player_data_pos += BFRIS_PNAME_POS; + player->name = strdup((char*)saved_data + player_data_pos); + + player_data_pos += strlen(player->name) + 1; + } + + } + + server->server_name = BFRIS_SERVER_NAME; + + return DONE_FORCE; +} + + diff --git a/reference/cryengine/gamedignote.txt b/reference/cryengine/gamedignote.txt index b3648f9..2b303f9 100644 --- a/reference/cryengine/gamedignote.txt +++ b/reference/cryengine/gamedignote.txt @@ -1,2 +1,2 @@ -I was under the impression all the crysis games used gamespy? -If anyone notices a problem, this is reference for some old cryengine protocol. +I was under the impression all the crysis games used gamespy? +If anyone notices a problem, this is reference for some old cryengine protocol. diff --git a/reference/cryengine/gameq.txt b/reference/cryengine/gameq.txt index 5c5dcab..7d64943 100644 --- a/reference/cryengine/gameq.txt +++ b/reference/cryengine/gameq.txt @@ -1,100 +1,100 @@ -. - * - * $Id: cry.php,v 1.2 2008/04/22 18:52:27 tombuskens Exp $ - */ - - -[cry] -rules = "\x7f\xff\xff\xffrules" -status = "\x7f\xff\xff\xffstatus" -players = "\x7f\xff\xff\xffplayers" - -[farcry2] -status = "\x06\x01\x00\x00\x2b\xbf\x53\x51\xdc\x80\x19\xb8\xb0\x57\xa3\x75" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * CryEngine protocol - * - * @author Tom Buskens - * @version $Revision: 1.2 $ - */ -class GameQ_Protocol_cry extends GameQ_Protocol -{ - public function rules() - { - // Header - $this->header(); - - // Rules - while ($this->p->getLength()) { - $this->r->add($this->p->readString(), $this->p->readString()); - } - } - - public function status() - { - // Header - $this->header(); - - // Unknown - $this->p->read(15); - - $this->r->add('hostname', $this->p->readString()); - $this->r->add('mod', $this->p->readString()); - $this->r->add('gametype', $this->p->readString()); - $this->r->add('map', $this->p->readString()); - - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('max_players', $this->p->readInt8()); - $this->r->add('password', $this->p->readInt8()); - $this->p->read(2); - $this->r->add('punkbuster', $this->p->readInt8()); - } - - - public function players() - { - $this->header(); - $this->p->skip(2); - - while ($this->p->getLength()) { - $this->r->addPlayer('name', $this->p->readString()); - $this->r->addPlayer('team', $this->p->readString()); - $this->p->skip(1); - $this->r->addPlayer('score', $this->p->readInt8()); - $this->p->skip(3); - $this->r->addPlayer('ping', $this->p->readInt8()); - $this->p->skip(7); - } - } - - - private function header() - { - if ($this->p->read(4) !== "\x7f\xff\xff\xff") { - throw new GameQ_ParsingException($this->p); - } - $this->p->skip(2); - } -} -?> - +. + * + * $Id: cry.php,v 1.2 2008/04/22 18:52:27 tombuskens Exp $ + */ + + +[cry] +rules = "\x7f\xff\xff\xffrules" +status = "\x7f\xff\xff\xffstatus" +players = "\x7f\xff\xff\xffplayers" + +[farcry2] +status = "\x06\x01\x00\x00\x2b\xbf\x53\x51\xdc\x80\x19\xb8\xb0\x57\xa3\x75" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * CryEngine protocol + * + * @author Tom Buskens + * @version $Revision: 1.2 $ + */ +class GameQ_Protocol_cry extends GameQ_Protocol +{ + public function rules() + { + // Header + $this->header(); + + // Rules + while ($this->p->getLength()) { + $this->r->add($this->p->readString(), $this->p->readString()); + } + } + + public function status() + { + // Header + $this->header(); + + // Unknown + $this->p->read(15); + + $this->r->add('hostname', $this->p->readString()); + $this->r->add('mod', $this->p->readString()); + $this->r->add('gametype', $this->p->readString()); + $this->r->add('map', $this->p->readString()); + + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('max_players', $this->p->readInt8()); + $this->r->add('password', $this->p->readInt8()); + $this->p->read(2); + $this->r->add('punkbuster', $this->p->readInt8()); + } + + + public function players() + { + $this->header(); + $this->p->skip(2); + + while ($this->p->getLength()) { + $this->r->addPlayer('name', $this->p->readString()); + $this->r->addPlayer('team', $this->p->readString()); + $this->p->skip(1); + $this->r->addPlayer('score', $this->p->readInt8()); + $this->p->skip(3); + $this->r->addPlayer('ping', $this->p->readInt8()); + $this->p->skip(7); + } + } + + + private function header() + { + if ($this->p->read(4) !== "\x7f\xff\xff\xff") { + throw new GameQ_ParsingException($this->p); + } + $this->p->skip(2); + } +} +?> + diff --git a/reference/cryengine/qstat.txt b/reference/cryengine/qstat.txt index 01a8f65..35496a1 100644 --- a/reference/cryengine/qstat.txt +++ b/reference/cryengine/qstat.txt @@ -1,326 +1,326 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - -{ - /* CRYSIS PROTOCOL */ - CRYSIS_PROTOCOL_SERVER, /* id */ - "CRYSIS", /* type_prefix */ - "crysis", /* type_string */ - "-crysis", /* type_option */ - "Crysis", /* game_name */ - 0, /* master */ - 0, /* default_port */ - 0, /* port_offset */ - TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */ - "gamerules", /* game_rule */ - "CRYSISPROTOCOL", /* template_var */ - NULL, /* status_packet */ - 0, /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - NULL, /* display_player_func */ - display_server_rules, /* display_rule_func */ - NULL, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_crysis_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_crysis_packet, /* packet_func */ -}, - - - -/* - * qstat 2.8 - * by Steve Jankowski - * - * Crysis query protocol - * Copyright 2012 Steven Hartland - * - * Licensed under the Artistic License, see LICENSE.txt for license terms - * - */ - -#include -#ifndef _WIN32 -#include -#include -#include -#else -#include -#endif -#include -#include -#include - -#include "debug.h" -#include "utils.h" -#include "qstat.h" -#include "md5.h" -#include "packet_manip.h" - -char *decode_crysis_val( char *val ) -{ - // Very basic html conversion - val = str_replace( val, """, "\"" ); - return str_replace( val, "&", "&" ); -} - -query_status_t send_crysis_request_packet( struct qserver *server ) -{ - char cmd[256], buf[1024], *password, *md5; - debug( 2, "challenge: %ld", server->challenge ); - switch ( server->challenge ) - { - case 0: - // Not seen a challenge yet, request it - server->challenge++; - sprintf( cmd, "challenge" ); - break; - - case 1: - server->challenge++; - password = get_param_value( server, "password", "" ); - sprintf( cmd, "%s:%s", server->challenge_string, password ); - md5 = md5_hex( cmd, strlen( cmd ) ); - sprintf( cmd, "authenticate %s", md5 ); - free( md5 ); - break; - - case 2: - // NOTE: we currently don't support player info - server->challenge++; - server->flags |= TF_STATUS_QUERY; - server->n_servers = 3; - sprintf( cmd, "status" ); - break; - - case 3: - return DONE_FORCE; - } - - server->saved_data.pkt_max = -1; - sprintf(buf, "POST /RPC2 HTTP/1.1\015\012Keep-Alive: 300\015\012User-Agent: qstat %s\015\012Content-Length: %d\015\012Content-Type: text/xml\015\012\015\012%s", VERSION, (int)(98 + strlen(cmd)), cmd); - - return send_packet( server, buf, strlen( buf ) ); -} - -query_status_t valid_crysis_response( struct qserver *server, char *rawpkt, int pktlen ) -{ - char *s; - int len; - int cnt = packet_count( server ); - if ( 0 == cnt && 0 != strncmp( "HTTP/1.1 200 OK", rawpkt, 15 ) ) - { - // not valid response - return REQ_ERROR; - } - - s = strnstr(rawpkt, "Content-Length: ", pktlen ); - if ( NULL == s ) - { - // not valid response - return INPROGRESS; - } - s += 16; - if ( 1 != sscanf( s, "%d", &len ) ) - { - return INPROGRESS; - } - - s = strnstr(rawpkt, "\015\012\015\012", pktlen ); - if ( NULL == s ) - { - return INPROGRESS; - } - - s += 4; - if ( pktlen != ( s - rawpkt + len ) ) - { - return INPROGRESS; - } - - return DONE_FORCE; -} - -char* crysis_response( struct qserver *server, char *rawpkt, int pktlen ) -{ - char *s, *e; - int len = pktlen; - - s = strnstr(rawpkt, "", len ); - if ( NULL == s ) - { - // not valid response - return NULL; - } - s += 46; - len += rawpkt - s; - e = strnstr(s, "", len ); - if ( NULL == e ) - { - // not valid response - return NULL; - } - *e = '\0'; - - return strdup( s ); -} - -query_status_t deal_with_crysis_packet( struct qserver *server, char *rawpkt, int pktlen ) -{ - char *s, *val, *line; - query_status_t state = INPROGRESS; - debug( 2, "processing..." ); - - if ( ! server->combined ) - { - state = valid_crysis_response( server, rawpkt, pktlen ); - server->retry1 = n_retries; - if ( 0 == server->n_requests ) - { - server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 ); - server->n_requests++; - } - - switch ( state ) - { - case INPROGRESS: - { - // response fragment recieved - int pkt_id; - int pkt_max; - - // We're expecting more to come - debug( 5, "fragment recieved..." ); - pkt_id = packet_count( server ); - pkt_max = pkt_id++; - if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) ) - { - // fatal error e.g. out of memory - return MEM_ERROR; - } - - // combine_packets will call us recursively - return combine_packets( server ); - } - case DONE_FORCE: - break; // single packet response fall through - default: - return state; - } - } - - if ( DONE_FORCE != state ) - { - state = valid_crysis_response( server, rawpkt, pktlen ); - switch ( state ) - { - case DONE_FORCE: - break; // actually process - default: - return state; - } - } - - debug( 3, "packet: challenge = %ld", server->challenge ); - switch ( server->challenge ) - { - case 1: - s = crysis_response( server, rawpkt, pktlen ); - if ( NULL != s ) - { - server->challenge_string = s; - return send_crysis_request_packet( server ); - } - return REQ_ERROR; - case 2: - s = crysis_response( server, rawpkt, pktlen ); - if ( NULL == s ) - { - return REQ_ERROR; - } - if ( 0 != strncmp( s, "authorized", 10 ) ) - { - free( s ); - return REQ_ERROR; - } - free( s ); - return send_crysis_request_packet( server ); - case 3: - s = crysis_response( server, rawpkt, pktlen ); - if ( NULL == s ) - { - return REQ_ERROR; - } - } - - // Correct ping - // Not quite right but gives a good estimate - server->ping_total = ( server->ping_total * server->n_requests ) / 2; - - debug( 3, "processing response..." ); - - s = decode_crysis_val( s ); - line = strtok( s, "\012" ); - - // NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of - while ( NULL != line ) - { - debug( 4, "LINE: %s\n", line ); - val = strstr( line, ":" ); - if ( NULL != val ) - { - *val = '\0'; - val+=2; - debug( 4, "var: %s, val: %s", line, val ); - if ( 0 == strcmp( "name", line ) ) - { - server->server_name = strdup( val ); - } - else if ( 0 == strcmp( "level", line ) ) - { - server->map_name = strdup( val ); - } - else if ( 0 == strcmp( "players", line ) ) - { - if ( 2 == sscanf( val, "%d/%d", &server->num_players, &server->max_players) ) - { - } - } - else if ( - 0 == strcmp( "version", line ) || - 0 == strcmp( "gamerules", line ) || - 0 == strcmp( "time remaining", line ) - ) - { - add_rule( server, line, val, NO_FLAGS ); - } - } - - line = strtok( NULL, "\012" ); - } - - gettimeofday( &server->packet_time1, NULL ); - - return DONE_FORCE; -} - - - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + +{ + /* CRYSIS PROTOCOL */ + CRYSIS_PROTOCOL_SERVER, /* id */ + "CRYSIS", /* type_prefix */ + "crysis", /* type_string */ + "-crysis", /* type_option */ + "Crysis", /* game_name */ + 0, /* master */ + 0, /* default_port */ + 0, /* port_offset */ + TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */ + "gamerules", /* game_rule */ + "CRYSISPROTOCOL", /* template_var */ + NULL, /* status_packet */ + 0, /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + NULL, /* display_player_func */ + display_server_rules, /* display_rule_func */ + NULL, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_crysis_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_crysis_packet, /* packet_func */ +}, + + + +/* + * qstat 2.8 + * by Steve Jankowski + * + * Crysis query protocol + * Copyright 2012 Steven Hartland + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + * + */ + +#include +#ifndef _WIN32 +#include +#include +#include +#else +#include +#endif +#include +#include +#include + +#include "debug.h" +#include "utils.h" +#include "qstat.h" +#include "md5.h" +#include "packet_manip.h" + +char *decode_crysis_val( char *val ) +{ + // Very basic html conversion + val = str_replace( val, """, "\"" ); + return str_replace( val, "&", "&" ); +} + +query_status_t send_crysis_request_packet( struct qserver *server ) +{ + char cmd[256], buf[1024], *password, *md5; + debug( 2, "challenge: %ld", server->challenge ); + switch ( server->challenge ) + { + case 0: + // Not seen a challenge yet, request it + server->challenge++; + sprintf( cmd, "challenge" ); + break; + + case 1: + server->challenge++; + password = get_param_value( server, "password", "" ); + sprintf( cmd, "%s:%s", server->challenge_string, password ); + md5 = md5_hex( cmd, strlen( cmd ) ); + sprintf( cmd, "authenticate %s", md5 ); + free( md5 ); + break; + + case 2: + // NOTE: we currently don't support player info + server->challenge++; + server->flags |= TF_STATUS_QUERY; + server->n_servers = 3; + sprintf( cmd, "status" ); + break; + + case 3: + return DONE_FORCE; + } + + server->saved_data.pkt_max = -1; + sprintf(buf, "POST /RPC2 HTTP/1.1\015\012Keep-Alive: 300\015\012User-Agent: qstat %s\015\012Content-Length: %d\015\012Content-Type: text/xml\015\012\015\012%s", VERSION, (int)(98 + strlen(cmd)), cmd); + + return send_packet( server, buf, strlen( buf ) ); +} + +query_status_t valid_crysis_response( struct qserver *server, char *rawpkt, int pktlen ) +{ + char *s; + int len; + int cnt = packet_count( server ); + if ( 0 == cnt && 0 != strncmp( "HTTP/1.1 200 OK", rawpkt, 15 ) ) + { + // not valid response + return REQ_ERROR; + } + + s = strnstr(rawpkt, "Content-Length: ", pktlen ); + if ( NULL == s ) + { + // not valid response + return INPROGRESS; + } + s += 16; + if ( 1 != sscanf( s, "%d", &len ) ) + { + return INPROGRESS; + } + + s = strnstr(rawpkt, "\015\012\015\012", pktlen ); + if ( NULL == s ) + { + return INPROGRESS; + } + + s += 4; + if ( pktlen != ( s - rawpkt + len ) ) + { + return INPROGRESS; + } + + return DONE_FORCE; +} + +char* crysis_response( struct qserver *server, char *rawpkt, int pktlen ) +{ + char *s, *e; + int len = pktlen; + + s = strnstr(rawpkt, "", len ); + if ( NULL == s ) + { + // not valid response + return NULL; + } + s += 46; + len += rawpkt - s; + e = strnstr(s, "", len ); + if ( NULL == e ) + { + // not valid response + return NULL; + } + *e = '\0'; + + return strdup( s ); +} + +query_status_t deal_with_crysis_packet( struct qserver *server, char *rawpkt, int pktlen ) +{ + char *s, *val, *line; + query_status_t state = INPROGRESS; + debug( 2, "processing..." ); + + if ( ! server->combined ) + { + state = valid_crysis_response( server, rawpkt, pktlen ); + server->retry1 = n_retries; + if ( 0 == server->n_requests ) + { + server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 ); + server->n_requests++; + } + + switch ( state ) + { + case INPROGRESS: + { + // response fragment recieved + int pkt_id; + int pkt_max; + + // We're expecting more to come + debug( 5, "fragment recieved..." ); + pkt_id = packet_count( server ); + pkt_max = pkt_id++; + if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) ) + { + // fatal error e.g. out of memory + return MEM_ERROR; + } + + // combine_packets will call us recursively + return combine_packets( server ); + } + case DONE_FORCE: + break; // single packet response fall through + default: + return state; + } + } + + if ( DONE_FORCE != state ) + { + state = valid_crysis_response( server, rawpkt, pktlen ); + switch ( state ) + { + case DONE_FORCE: + break; // actually process + default: + return state; + } + } + + debug( 3, "packet: challenge = %ld", server->challenge ); + switch ( server->challenge ) + { + case 1: + s = crysis_response( server, rawpkt, pktlen ); + if ( NULL != s ) + { + server->challenge_string = s; + return send_crysis_request_packet( server ); + } + return REQ_ERROR; + case 2: + s = crysis_response( server, rawpkt, pktlen ); + if ( NULL == s ) + { + return REQ_ERROR; + } + if ( 0 != strncmp( s, "authorized", 10 ) ) + { + free( s ); + return REQ_ERROR; + } + free( s ); + return send_crysis_request_packet( server ); + case 3: + s = crysis_response( server, rawpkt, pktlen ); + if ( NULL == s ) + { + return REQ_ERROR; + } + } + + // Correct ping + // Not quite right but gives a good estimate + server->ping_total = ( server->ping_total * server->n_requests ) / 2; + + debug( 3, "processing response..." ); + + s = decode_crysis_val( s ); + line = strtok( s, "\012" ); + + // NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of + while ( NULL != line ) + { + debug( 4, "LINE: %s\n", line ); + val = strstr( line, ":" ); + if ( NULL != val ) + { + *val = '\0'; + val+=2; + debug( 4, "var: %s, val: %s", line, val ); + if ( 0 == strcmp( "name", line ) ) + { + server->server_name = strdup( val ); + } + else if ( 0 == strcmp( "level", line ) ) + { + server->map_name = strdup( val ); + } + else if ( 0 == strcmp( "players", line ) ) + { + if ( 2 == sscanf( val, "%d/%d", &server->num_players, &server->max_players) ) + { + } + } + else if ( + 0 == strcmp( "version", line ) || + 0 == strcmp( "gamerules", line ) || + 0 == strcmp( "time remaining", line ) + ) + { + add_rule( server, line, val, NO_FLAGS ); + } + } + + line = strtok( NULL, "\012" ); + } + + gettimeofday( &server->packet_time1, NULL ); + + return DONE_FORCE; +} + + + diff --git a/reference/cs2d/gameq.txt b/reference/cs2d/gameq.txt index fe73696..16a27d1 100644 --- a/reference/cs2d/gameq.txt +++ b/reference/cs2d/gameq.txt @@ -1,56 +1,56 @@ -. - * - * $Id: cs2d.php,v 1.1 2008/04/14 18:04:50 tombuskens Exp $ - */ - -[cs2d] -status = "\xfa\x00" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Counterstrike 2d Protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_cs2d extends GameQ_Protocol -{ - public function status() - { - $this->p->skip(2); - $this->r->add('hostname', $this->readString()); - $this->r->add('password', $this->p->readInt8()); - $this->r->add('mapname', $this->readString()); - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('max_players', $this->p->readInt8()); - $this->r->add('fog_of_war', $this->p->readInt8()); - $this->r->add('war_mode', $this->p->readInt8()); - $this->r->add('version', $this->readString()); - } - - private function readString() - { - $str = $this->p->readString("\x0D"); - $this->p->skip(1); - return $str; - } -} -?> +. + * + * $Id: cs2d.php,v 1.1 2008/04/14 18:04:50 tombuskens Exp $ + */ + +[cs2d] +status = "\xfa\x00" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Counterstrike 2d Protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_cs2d extends GameQ_Protocol +{ + public function status() + { + $this->p->skip(2); + $this->r->add('hostname', $this->readString()); + $this->r->add('password', $this->p->readInt8()); + $this->r->add('mapname', $this->readString()); + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('max_players', $this->p->readInt8()); + $this->r->add('fog_of_war', $this->p->readInt8()); + $this->r->add('war_mode', $this->p->readInt8()); + $this->r->add('version', $this->readString()); + } + + private function readString() + { + $str = $this->p->readString("\x0D"); + $this->p->skip(1); + return $str; + } +} +?> diff --git a/reference/cs2d/lgsl.txt b/reference/cs2d/lgsl.txt index 259ade0..8258ff3 100644 --- a/reference/cs2d/lgsl.txt +++ b/reference/cs2d/lgsl.txt @@ -1,70 +1,70 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_29(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: http://www.cs2d.com/servers.php - - if ($lgsl_need['s'] || $lgsl_need['e']) - { - $lgsl_need['s'] = FALSE; - $lgsl_need['e'] = FALSE; - - fwrite($lgsl_fp, "\x01\x00\x03\x10\x21\xFB\x01\x75\x00"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - - $buffer = substr($buffer, 4); // REMOVE HEADER - - $server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1)) - 48; - $server['s']['name'] = lgsl_cut_pascal($buffer); - $server['s']['map'] = lgsl_cut_pascal($buffer); - $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['bots'] = ord(lgsl_cut_byte($buffer, 1)); - - $server['s']['password'] = ($server['e']['bit_flags'] & 1) ? "1" : "0"; - $server['e']['registered_only'] = ($server['e']['bit_flags'] & 2) ? "1" : "0"; - $server['e']['fog_of_war'] = ($server['e']['bit_flags'] & 4) ? "1" : "0"; - $server['e']['friendlyfire'] = ($server['e']['bit_flags'] & 8) ? "1" : "0"; - } - - if ($lgsl_need['p']) - { - $lgsl_need['p'] = FALSE; - - fwrite($lgsl_fp, "\x01\x00\xFB\x05"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - - $buffer = substr($buffer, 4); // REMOVE HEADER - - $player_total = ord(lgsl_cut_byte($buffer, 1)); - - for ($i=0; $i<$player_total; $i++) - { - $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); - $server['p'][$i]['name'] = lgsl_cut_pascal($buffer); - $server['p'][$i]['team'] = ord(lgsl_cut_byte($buffer, 1)); - $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); - $server['p'][$i]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); - } - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + function lgsl_query_29(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: http://www.cs2d.com/servers.php + + if ($lgsl_need['s'] || $lgsl_need['e']) + { + $lgsl_need['s'] = FALSE; + $lgsl_need['e'] = FALSE; + + fwrite($lgsl_fp, "\x01\x00\x03\x10\x21\xFB\x01\x75\x00"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + + $buffer = substr($buffer, 4); // REMOVE HEADER + + $server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1)) - 48; + $server['s']['name'] = lgsl_cut_pascal($buffer); + $server['s']['map'] = lgsl_cut_pascal($buffer); + $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['bots'] = ord(lgsl_cut_byte($buffer, 1)); + + $server['s']['password'] = ($server['e']['bit_flags'] & 1) ? "1" : "0"; + $server['e']['registered_only'] = ($server['e']['bit_flags'] & 2) ? "1" : "0"; + $server['e']['fog_of_war'] = ($server['e']['bit_flags'] & 4) ? "1" : "0"; + $server['e']['friendlyfire'] = ($server['e']['bit_flags'] & 8) ? "1" : "0"; + } + + if ($lgsl_need['p']) + { + $lgsl_need['p'] = FALSE; + + fwrite($lgsl_fp, "\x01\x00\xFB\x05"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + + $buffer = substr($buffer, 4); // REMOVE HEADER + + $player_total = ord(lgsl_cut_byte($buffer, 1)); + + for ($i=0; $i<$player_total; $i++) + { + $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); + $server['p'][$i]['name'] = lgsl_cut_pascal($buffer); + $server['p'][$i]['team'] = ord(lgsl_cut_byte($buffer, 1)); + $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); + $server['p'][$i]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); + } + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/cube/gameq_cube.txt b/reference/cube/gameq_cube.txt index 5320a80..39a4fca 100644 --- a/reference/cube/gameq_cube.txt +++ b/reference/cube/gameq_cube.txt @@ -1,58 +1,58 @@ -. - * - * $Id: cube.php,v 1.1 2007/07/04 09:08:36 tombuskens Exp $ - */ - -[cube] -status = "\x00" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Cube Engine protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_cube extends GameQ_Protocol -{ - /* - * status packet - */ - public function status() - { - // Header - if (!$this->p->read() == "\x00") { - throw new GameQ_ParsingException($this->p); - } - $this->p->skip(2); - - // Vars - $this->r->add('protocol', $this->p->readInt8()); - $this->r->add('mode', $this->p->readInt8()); - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('time_remaining', $this->p->readInt8()); - $this->r->add('map', $this->p->readString()); - $this->r->add('servername', $this->p->readString()); - $this->r->add('max_players', $this->p->readInt8()); - } - -} -?> +. + * + * $Id: cube.php,v 1.1 2007/07/04 09:08:36 tombuskens Exp $ + */ + +[cube] +status = "\x00" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Cube Engine protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_cube extends GameQ_Protocol +{ + /* + * status packet + */ + public function status() + { + // Header + if (!$this->p->read() == "\x00") { + throw new GameQ_ParsingException($this->p); + } + $this->p->skip(2); + + // Vars + $this->r->add('protocol', $this->p->readInt8()); + $this->r->add('mode', $this->p->readInt8()); + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('time_remaining', $this->p->readInt8()); + $this->r->add('map', $this->p->readString()); + $this->r->add('servername', $this->p->readString()); + $this->r->add('max_players', $this->p->readInt8()); + } + +} +?> diff --git a/reference/cube/gameq_sauerbraten.txt b/reference/cube/gameq_sauerbraten.txt index d313202..f53c2ae 100644 --- a/reference/cube/gameq_sauerbraten.txt +++ b/reference/cube/gameq_sauerbraten.txt @@ -1,79 +1,79 @@ -. - * - * $Id: sauerbraten.php,v 1.2 2008/06/25 13:50:47 tombuskens Exp $ - */ - -[sauerbraten] -status = "\xFF" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Sauerbraten / Cube 2 Engine protocol - * - * @author Tom Buskens - * @version $Revision: 1.2 $ - */ -class GameQ_Protocol_sauerbraten extends GameQ_Protocol -{ - private function getint() - { - $i = $this->p->readInt8(); - if ($i == 0x80) - { - $i = $this->p->readInt8(); - $i |= $this->p->readInt8() << 8; - } - else if ($i == 0x81) - { - $i = $this->p->readInt8(); - $i |= $this->p->readInt8() << 8; - $i |= $this->p->readInt8() << 16; - $i |= $this->p->readInt8() << 24; - } - - return $i; - } - - /* - * status packet - */ - public function status() - { - // Header - if (!$this->p->read() == "\x00") { - throw new GameQ_ParsingException($this->p); - } - - // Vars - $this->r->add('num_players', $this->getint()); - $this->r->add('num_attributes', $this->getint()); - $this->r->add('protocol', $this->getint()); - $this->r->add('servermode', $this->getint()); - $this->r->add('time_remaining', $this->getint()); - $this->r->add('max_players', $this->getint()); - $this->r->add('locked', $this->getint()); - $this->r->add('map', $this->p->readString()); - $this->r->add('servername', $this->p->readString()); - } -} - - -?> +. + * + * $Id: sauerbraten.php,v 1.2 2008/06/25 13:50:47 tombuskens Exp $ + */ + +[sauerbraten] +status = "\xFF" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Sauerbraten / Cube 2 Engine protocol + * + * @author Tom Buskens + * @version $Revision: 1.2 $ + */ +class GameQ_Protocol_sauerbraten extends GameQ_Protocol +{ + private function getint() + { + $i = $this->p->readInt8(); + if ($i == 0x80) + { + $i = $this->p->readInt8(); + $i |= $this->p->readInt8() << 8; + } + else if ($i == 0x81) + { + $i = $this->p->readInt8(); + $i |= $this->p->readInt8() << 8; + $i |= $this->p->readInt8() << 16; + $i |= $this->p->readInt8() << 24; + } + + return $i; + } + + /* + * status packet + */ + public function status() + { + // Header + if (!$this->p->read() == "\x00") { + throw new GameQ_ParsingException($this->p); + } + + // Vars + $this->r->add('num_players', $this->getint()); + $this->r->add('num_attributes', $this->getint()); + $this->r->add('protocol', $this->getint()); + $this->r->add('servermode', $this->getint()); + $this->r->add('time_remaining', $this->getint()); + $this->r->add('max_players', $this->getint()); + $this->r->add('locked', $this->getint()); + $this->r->add('map', $this->p->readString()); + $this->r->add('servername', $this->p->readString()); + } +} + + +?> diff --git a/reference/cube/lgsl.txt b/reference/cube/lgsl.txt index ca60d1f..ab7fddc 100644 --- a/reference/cube/lgsl.txt +++ b/reference/cube/lgsl.txt @@ -1,168 +1,168 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_24(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: http://cubelister.sourceforge.net - - fwrite($lgsl_fp, "\x21\x21"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - - $buffer = substr($buffer, 2); // REMOVE HEADER - -//---------------------------------------------------------+ - - if ($buffer[0] == "\x1b") // CUBE 1 - { - // RESPONSE IS XOR ENCODED FOR SOME STRANGE REASON - for ($i=0; $i -#include -#include - -#include "debug.h" -#include "qstat.h" -#include "packet_manip.h" - -struct offset -{ - unsigned char *d; - int pos; - int len; -}; - - -//#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list" -#define SB_PROTOCOL 258 -#define MIN(a,b) ((a)<(b)?(a):(b)) -#define MAX_ATTR 255 -#define MAX_STRING 1024 - - -static int -getint (struct offset * d) -{ - int val = 0; - - if ( d->pos >= d->len ) - { - return 0; - } - - val = d->d[d->pos++] & 0xff; // 8 bit value - - // except... - if ( val == 0x80 && d->pos < d->len - 2 ) // 16 bit value - { - val = (d->d[d->pos++] & 0xff); - val |= (d->d[d->pos++] & 0xff) << 8; - } - else if ( val == 0x81 && d->pos < d->len - 4 ) // 32 bit value - { - val = (d->d[d->pos++] & 0xff); - val |= (d->d[d->pos++] & 0xff) << 8; - val |= (d->d[d->pos++] & 0xff) << 16; - val |= (d->d[d->pos++] & 0xff) << 24; - } - - return val; -} - - -static char * getstr( char *dest, int dest_len, struct offset *d ) -{ - int len = 0; - - if (d->pos >= d->len) - { - return NULL; - } - - len = MIN( dest_len, d->len - d->pos ); - strncpy( dest, (const char *) d->d + d->pos, len )[len - 1] = 0; - d->pos += strlen (dest) + 1; - - return dest; -} - - -static char* sb_getversion_s (int n) -{ - static char *version_s[] = - { - "Justice", - "CTF", - "Assassin", - "Summer", - "Spring", - "Gui", - "Water", - "Normalmap", - "Sp", - "Occlusion", - "Shader", - "Physics", - "Mp", - "", - "Agc", - "Quakecon", - "Independence" - }; - - n = SB_PROTOCOL - n; - if (n >= 0 && (size_t) n < sizeof(version_s) / sizeof(version_s[0])) - { - return version_s[n]; - } - return "unknown"; -} - - -static char* sb_getmode_s(int n) -{ - static char *mode_s[] = - { - "slowmo SP", - "slowmo DMSP", - "demo", - "SP", - "DMSP", - "ffa/default", - "coopedit", - "ffa/duel", - "teamplay", - "instagib", - "instagib team", - "efficiency", - "efficiency team", - "insta arena", - "insta clan arena", - "tactics arena", - "tactics clan arena", - "capture", - "insta capture", - "regen capture", - "assassin", - "insta assassin", - "ctf", - "insta ctf" - }; - - n += 6; - if (n >= 0 && (size_t) n < sizeof(mode_s) / sizeof(mode_s[0])) - { - return mode_s[n]; - } - return "unknown"; -} - - -query_status_t send_cube2_request_packet( struct qserver *server ) -{ - return send_packet( server, server->type->status_packet, server->type->status_len ); -} - - -query_status_t deal_with_cube2_packet( struct qserver *server, char *rawpkt, int pktlen ) -{ - // skip unimplemented ack, crc, etc - int i; - int numattr; - int attr[MAX_ATTR]; - char buf[MAX_STRING]; - enum { - MM_OPEN = 0, - MM_VETO, - MM_LOCKED, - MM_PRIVATE - }; - struct offset d; - d.d = (unsigned char *) rawpkt; - d.pos = 0; - d.len = pktlen; - - server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 ); - getint( &d ); // we have the ping already - server->num_players = getint( &d ); - numattr = getint( &d ); - for ( i = 0; i < numattr && i < MAX_ATTR; i++ ) - { - attr[i] = getint (&d); - } - - server->protocol_version = attr[0]; - - sprintf( buf, "%d %s", attr[0], sb_getversion_s (attr[0]) ); - add_rule( server, "version", buf, NO_FLAGS ); - - sprintf( buf, "%d %s", attr[1], sb_getmode_s (attr[1]) ); - add_rule( server, "mode", buf, NO_FLAGS ); - - sprintf( buf, "%d", attr[2] ); - add_rule( server, "seconds_left", buf, NO_FLAGS ); - - server->max_players = attr[3]; - - switch ( attr[5] ) - { - case MM_OPEN: - sprintf( buf, "%d open", attr[5] ); - break; - case MM_VETO: - sprintf( buf, "%d veto", attr[5] ); - break; - case MM_LOCKED: - sprintf( buf, "%d locked", attr[5] ); - break; - case MM_PRIVATE: - sprintf( buf, "%d private", attr[5] ); - break; - default: - sprintf( buf, "%d unknown", attr[5] ); - } - add_rule( server, "mm", buf, NO_FLAGS); - - for ( i = 0; i < numattr && i < MAX_ATTR; i++ ) - { - char buf2[MAX_STRING]; - sprintf( buf, "attr%d", i ); - sprintf( buf2, "%d", attr[i] ); - add_rule( server, buf, buf2, NO_FLAGS ); - } - - getstr( buf, MAX_STRING, &d ); - server->map_name = strdup(buf); - getstr( buf, MAX_STRING, &d ); - server->server_name = strdup(buf); - - return DONE_FORCE; -} - - - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + char cube2_serverstatus[3] = {'\x80', '\x10', '\x27'}; + + + { + /* Cube 2/Sauerbraten/Blood Frontier */ + CUBE2_SERVER, /* id */ + "CUBE2", /* type_prefix */ + "cube2", /* type_string */ + "-cubes", /* type_option */ + "Sauerbraten", /* game_name */ + 0, /* master */ + CUBE2_DEFAULT_PORT, /* default_port */ + 1, /* port_offset */ + 0, /* flags */ + "", /* game_rule */ + "CUBE2", /* template_var */ + cube2_serverstatus, /* status_packet */ + sizeof(cube2_serverstatus), /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + NULL, /* display_player_func */ + display_server_rules, /* display_rule_func */ + NULL, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + NULL, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_cube2_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_cube2_packet, /* packet_func */ +}, + + + +/* + * qstat 2.12 + * by Steve Jankowski + * + * Cube 2 / Sauerbraten protocol + * Copyright 2011 NoisyB + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + */ +#include +#include +#include + +#include "debug.h" +#include "qstat.h" +#include "packet_manip.h" + +struct offset +{ + unsigned char *d; + int pos; + int len; +}; + + +//#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list" +#define SB_PROTOCOL 258 +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX_ATTR 255 +#define MAX_STRING 1024 + + +static int +getint (struct offset * d) +{ + int val = 0; + + if ( d->pos >= d->len ) + { + return 0; + } + + val = d->d[d->pos++] & 0xff; // 8 bit value + + // except... + if ( val == 0x80 && d->pos < d->len - 2 ) // 16 bit value + { + val = (d->d[d->pos++] & 0xff); + val |= (d->d[d->pos++] & 0xff) << 8; + } + else if ( val == 0x81 && d->pos < d->len - 4 ) // 32 bit value + { + val = (d->d[d->pos++] & 0xff); + val |= (d->d[d->pos++] & 0xff) << 8; + val |= (d->d[d->pos++] & 0xff) << 16; + val |= (d->d[d->pos++] & 0xff) << 24; + } + + return val; +} + + +static char * getstr( char *dest, int dest_len, struct offset *d ) +{ + int len = 0; + + if (d->pos >= d->len) + { + return NULL; + } + + len = MIN( dest_len, d->len - d->pos ); + strncpy( dest, (const char *) d->d + d->pos, len )[len - 1] = 0; + d->pos += strlen (dest) + 1; + + return dest; +} + + +static char* sb_getversion_s (int n) +{ + static char *version_s[] = + { + "Justice", + "CTF", + "Assassin", + "Summer", + "Spring", + "Gui", + "Water", + "Normalmap", + "Sp", + "Occlusion", + "Shader", + "Physics", + "Mp", + "", + "Agc", + "Quakecon", + "Independence" + }; + + n = SB_PROTOCOL - n; + if (n >= 0 && (size_t) n < sizeof(version_s) / sizeof(version_s[0])) + { + return version_s[n]; + } + return "unknown"; +} + + +static char* sb_getmode_s(int n) +{ + static char *mode_s[] = + { + "slowmo SP", + "slowmo DMSP", + "demo", + "SP", + "DMSP", + "ffa/default", + "coopedit", + "ffa/duel", + "teamplay", + "instagib", + "instagib team", + "efficiency", + "efficiency team", + "insta arena", + "insta clan arena", + "tactics arena", + "tactics clan arena", + "capture", + "insta capture", + "regen capture", + "assassin", + "insta assassin", + "ctf", + "insta ctf" + }; + + n += 6; + if (n >= 0 && (size_t) n < sizeof(mode_s) / sizeof(mode_s[0])) + { + return mode_s[n]; + } + return "unknown"; +} + + +query_status_t send_cube2_request_packet( struct qserver *server ) +{ + return send_packet( server, server->type->status_packet, server->type->status_len ); +} + + +query_status_t deal_with_cube2_packet( struct qserver *server, char *rawpkt, int pktlen ) +{ + // skip unimplemented ack, crc, etc + int i; + int numattr; + int attr[MAX_ATTR]; + char buf[MAX_STRING]; + enum { + MM_OPEN = 0, + MM_VETO, + MM_LOCKED, + MM_PRIVATE + }; + struct offset d; + d.d = (unsigned char *) rawpkt; + d.pos = 0; + d.len = pktlen; + + server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 ); + getint( &d ); // we have the ping already + server->num_players = getint( &d ); + numattr = getint( &d ); + for ( i = 0; i < numattr && i < MAX_ATTR; i++ ) + { + attr[i] = getint (&d); + } + + server->protocol_version = attr[0]; + + sprintf( buf, "%d %s", attr[0], sb_getversion_s (attr[0]) ); + add_rule( server, "version", buf, NO_FLAGS ); + + sprintf( buf, "%d %s", attr[1], sb_getmode_s (attr[1]) ); + add_rule( server, "mode", buf, NO_FLAGS ); + + sprintf( buf, "%d", attr[2] ); + add_rule( server, "seconds_left", buf, NO_FLAGS ); + + server->max_players = attr[3]; + + switch ( attr[5] ) + { + case MM_OPEN: + sprintf( buf, "%d open", attr[5] ); + break; + case MM_VETO: + sprintf( buf, "%d veto", attr[5] ); + break; + case MM_LOCKED: + sprintf( buf, "%d locked", attr[5] ); + break; + case MM_PRIVATE: + sprintf( buf, "%d private", attr[5] ); + break; + default: + sprintf( buf, "%d unknown", attr[5] ); + } + add_rule( server, "mm", buf, NO_FLAGS); + + for ( i = 0; i < numattr && i < MAX_ATTR; i++ ) + { + char buf2[MAX_STRING]; + sprintf( buf, "attr%d", i ); + sprintf( buf2, "%d", attr[i] ); + add_rule( server, buf, buf2, NO_FLAGS ); + } + + getstr( buf, MAX_STRING, &d ); + server->map_name = strdup(buf); + getstr( buf, MAX_STRING, &d ); + server->server_name = strdup(buf); + + return DONE_FORCE; +} + + + diff --git a/reference/descent3/gamedignote.txt b/reference/descent3/gamedignote.txt index 5ed3d07..733784a 100644 --- a/reference/descent3/gamedignote.txt +++ b/reference/descent3/gamedignote.txt @@ -1,2 +1,2 @@ -This is the doc for some descent 3 protocol that ISNT the gamespy -protocol. Not really sure what it's for. +This is the doc for some descent 3 protocol that ISNT the gamespy +protocol. Not really sure what it's for. diff --git a/reference/descent3/qstat.txt b/reference/descent3/qstat.txt index 1b060e4..1978ed6 100644 --- a/reference/descent3/qstat.txt +++ b/reference/descent3/qstat.txt @@ -1,223 +1,223 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - -/* for some reason Descent3 uses a different request for pxo/non-pxo games. blah. */ -unsigned char descent3_pxoinfoquery[] = { - 0x01, /* "internal descent3 routing" */ - 0x29, /* request server info? (pxo listed servers) */ - 0x0b, 0x00, /* packet length (- routing byte) */ - 0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */ -}; -unsigned char descent3_tcpipinfoquery[] = { - 0x01, /* "internal descent3 routing" */ - 0x1e, /* request server info? (tcpip only servers) */ - 0x0b, 0x00, /* packet length (- routing byte) */ - 0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */ -}; -/* http://ml.warpcore.org/d3dl/200101/msg00001.html - * http://ml.warpcore.org/d3dl/200101/msg00004.html */ -unsigned char descent3_playerquery[] = { - 0x01, /* "internal descent3 routing" */ - 0x72, /* MP_REQUEST_PLAYERLIST */ - 0x03, 0x00 /* packet length (- routing byte) */ -}; - - - - -{ - /* DESCENT3 PROTOCOL */ - DESCENT3_SERVER, /* id */ - "D3S", /* type_prefix */ - "d3s", /* type_string */ - "-d3s", /* type_option */ - "Descent3", /* game_name */ - 0, /* master */ - DESCENT3_DEFAULT_PORT, /* default_port */ - 0, /* port_offset */ - 0, /* flags */ - "gametype", /* game_rule */ - "DESCENT3", /* template_var */ - (char*) &descent3_tcpipinfoquery, /* status_packet */ - sizeof( descent3_tcpipinfoquery), /* status_len */ - (char*) &descent3_playerquery, /* player_packet */ - sizeof( descent3_playerquery), /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_descent3_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_descent3_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_descent3_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_gps_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_descent3_packet, /* packet_func */ -}, -{ - /* DESCENT3 PROTOCOL */ - DESCENT3_PXO_SERVER, /* id */ - "D3P", /* type_prefix */ - "d3p", /* type_string */ - "-d3p", /* type_option */ - "Descent3 PXO protocol", /* game_name */ - 0, /* master */ - DESCENT3_DEFAULT_PORT, /* default_port */ - 0, /* port_offset */ - 0, /* flags */ - "gametype", /* game_rule */ - "DESCENT3", /* template_var */ - (char*) &descent3_pxoinfoquery, /* status_packet */ - sizeof( descent3_pxoinfoquery), /* status_len */ - (char*) &descent3_playerquery, /* player_packet */ - sizeof( descent3_playerquery), /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_descent3_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_descent3_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_descent3_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_gps_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_descent3_packet, /* packet_func */ -}, - - - - -query_status_t deal_with_descent3_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - char *pkt; - char buf[24]; - - debug( 2, "deal_with_descent3_packet %p, %d", server, pktlen ); - - if (server->server_name == NULL) - { - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - } - - if (pktlen < 4) - { - fprintf(stderr, "short descent3 packet\n"); - print_packet(server, rawpkt, pktlen); - return PKT_ERROR; - } - - /* 'info' response */ - if (rawpkt[1] == 0x1f) - { - if (server->server_name != NULL) - { - return PKT_ERROR; - } - - - pkt = &rawpkt[0x15]; - server->server_name = strdup(pkt); - pkt += strlen(pkt) + 2; - server->map_name = strdup(pkt); /* mission name (blah.mn3) */ - pkt += strlen(pkt) + 2; - add_rule(server, "level_name", pkt, NO_FLAGS); - pkt += strlen(pkt) + 2; - add_rule(server, "gametype", pkt, NO_FLAGS); - pkt += strlen(pkt) + 1; - - sprintf(buf, "%hu", swap_short_from_little(pkt)); - add_rule(server, "level_num", buf, NO_FLAGS); - pkt += 2; - server->num_players = swap_short_from_little(pkt); - pkt += 2; - server->max_players = swap_short_from_little(pkt); - pkt += 2; - - /* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */ - add_uchar_rule(server, "u0", pkt[0]); - add_uchar_rule(server, "u1", pkt[1]); - add_uchar_rule(server, "u2", pkt[2]); - add_uchar_rule(server, "u3", pkt[3]); - add_uchar_rule(server, "u4", pkt[4]); - add_uchar_rule(server, "u5", pkt[5]); - add_uchar_rule(server, "u6", pkt[6]); - add_uchar_rule(server, "u7", pkt[7]); - add_uchar_rule(server, "u8", pkt[8]); - - add_uchar_rule(server, "randpowerup", (unsigned char)!(pkt[4] &1)); /* - randomize powerup spawn */ - add_uchar_rule(server, "acccollisions", (unsigned char)((pkt[5] &4) > 0)); - /* accurate collision detection */ - add_uchar_rule(server, "brightships", (unsigned char)((pkt[5] &16) > 0)); - /* bright player ships */ - add_uchar_rule(server, "mouselook", (unsigned char)((pkt[6] &1) > 0)); /* - mouselook enabled */ - sprintf(buf, "%s%s", (pkt[4] &16) ? "PP" : "CS", (pkt[6] &1) ? "-ML" : ""); - add_rule(server, "servertype", buf, NO_FLAGS); - - sprintf(buf, "%hhu", pkt[9]); - add_rule(server, "difficulty", buf, NO_FLAGS); - - /* unknown/undecoded fields after known flags removed */ - add_uchar_rule(server, "x4", (unsigned char)(pkt[4] &~(1+16))); - add_uchar_rule(server, "x5", (unsigned char)(pkt[5] &~(4+16))); - add_uchar_rule(server, "x6", (unsigned char)(pkt[6] &~1)); - - if (get_player_info && server->num_players) - { - server->next_player_info = 0; - send_player_request_packet(server); - return INPROGRESS; - } - - } - /* MP_PLAYERLIST_DATA */ - else if (rawpkt[1] == 0x73) - { - struct player *player; - struct player **last_player = &server->players; - - if (server->players != NULL) - { - return PKT_ERROR; - } - - pkt = &rawpkt[0x4]; - while (*pkt) - { - player = (struct player*)calloc(1, sizeof(struct player)); - player->name = strdup(pkt); - pkt += strlen(pkt) + 1; - *last_player = player; - last_player = &player->next; - } - server->next_player_info = NO_PLAYER_INFO; - } - else - { - fprintf(stderr, "unknown d3 packet\n"); - print_packet(server, rawpkt, pktlen); - } - - return DONE_FORCE; -} - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + +/* for some reason Descent3 uses a different request for pxo/non-pxo games. blah. */ +unsigned char descent3_pxoinfoquery[] = { + 0x01, /* "internal descent3 routing" */ + 0x29, /* request server info? (pxo listed servers) */ + 0x0b, 0x00, /* packet length (- routing byte) */ + 0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */ +}; +unsigned char descent3_tcpipinfoquery[] = { + 0x01, /* "internal descent3 routing" */ + 0x1e, /* request server info? (tcpip only servers) */ + 0x0b, 0x00, /* packet length (- routing byte) */ + 0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */ +}; +/* http://ml.warpcore.org/d3dl/200101/msg00001.html + * http://ml.warpcore.org/d3dl/200101/msg00004.html */ +unsigned char descent3_playerquery[] = { + 0x01, /* "internal descent3 routing" */ + 0x72, /* MP_REQUEST_PLAYERLIST */ + 0x03, 0x00 /* packet length (- routing byte) */ +}; + + + + +{ + /* DESCENT3 PROTOCOL */ + DESCENT3_SERVER, /* id */ + "D3S", /* type_prefix */ + "d3s", /* type_string */ + "-d3s", /* type_option */ + "Descent3", /* game_name */ + 0, /* master */ + DESCENT3_DEFAULT_PORT, /* default_port */ + 0, /* port_offset */ + 0, /* flags */ + "gametype", /* game_rule */ + "DESCENT3", /* template_var */ + (char*) &descent3_tcpipinfoquery, /* status_packet */ + sizeof( descent3_tcpipinfoquery), /* status_len */ + (char*) &descent3_playerquery, /* player_packet */ + sizeof( descent3_playerquery), /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_descent3_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_descent3_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_descent3_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_gps_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_descent3_packet, /* packet_func */ +}, +{ + /* DESCENT3 PROTOCOL */ + DESCENT3_PXO_SERVER, /* id */ + "D3P", /* type_prefix */ + "d3p", /* type_string */ + "-d3p", /* type_option */ + "Descent3 PXO protocol", /* game_name */ + 0, /* master */ + DESCENT3_DEFAULT_PORT, /* default_port */ + 0, /* port_offset */ + 0, /* flags */ + "gametype", /* game_rule */ + "DESCENT3", /* template_var */ + (char*) &descent3_pxoinfoquery, /* status_packet */ + sizeof( descent3_pxoinfoquery), /* status_len */ + (char*) &descent3_playerquery, /* player_packet */ + sizeof( descent3_playerquery), /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_descent3_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_descent3_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_descent3_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_gps_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_descent3_packet, /* packet_func */ +}, + + + + +query_status_t deal_with_descent3_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + char *pkt; + char buf[24]; + + debug( 2, "deal_with_descent3_packet %p, %d", server, pktlen ); + + if (server->server_name == NULL) + { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } + + if (pktlen < 4) + { + fprintf(stderr, "short descent3 packet\n"); + print_packet(server, rawpkt, pktlen); + return PKT_ERROR; + } + + /* 'info' response */ + if (rawpkt[1] == 0x1f) + { + if (server->server_name != NULL) + { + return PKT_ERROR; + } + + + pkt = &rawpkt[0x15]; + server->server_name = strdup(pkt); + pkt += strlen(pkt) + 2; + server->map_name = strdup(pkt); /* mission name (blah.mn3) */ + pkt += strlen(pkt) + 2; + add_rule(server, "level_name", pkt, NO_FLAGS); + pkt += strlen(pkt) + 2; + add_rule(server, "gametype", pkt, NO_FLAGS); + pkt += strlen(pkt) + 1; + + sprintf(buf, "%hu", swap_short_from_little(pkt)); + add_rule(server, "level_num", buf, NO_FLAGS); + pkt += 2; + server->num_players = swap_short_from_little(pkt); + pkt += 2; + server->max_players = swap_short_from_little(pkt); + pkt += 2; + + /* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */ + add_uchar_rule(server, "u0", pkt[0]); + add_uchar_rule(server, "u1", pkt[1]); + add_uchar_rule(server, "u2", pkt[2]); + add_uchar_rule(server, "u3", pkt[3]); + add_uchar_rule(server, "u4", pkt[4]); + add_uchar_rule(server, "u5", pkt[5]); + add_uchar_rule(server, "u6", pkt[6]); + add_uchar_rule(server, "u7", pkt[7]); + add_uchar_rule(server, "u8", pkt[8]); + + add_uchar_rule(server, "randpowerup", (unsigned char)!(pkt[4] &1)); /* + randomize powerup spawn */ + add_uchar_rule(server, "acccollisions", (unsigned char)((pkt[5] &4) > 0)); + /* accurate collision detection */ + add_uchar_rule(server, "brightships", (unsigned char)((pkt[5] &16) > 0)); + /* bright player ships */ + add_uchar_rule(server, "mouselook", (unsigned char)((pkt[6] &1) > 0)); /* + mouselook enabled */ + sprintf(buf, "%s%s", (pkt[4] &16) ? "PP" : "CS", (pkt[6] &1) ? "-ML" : ""); + add_rule(server, "servertype", buf, NO_FLAGS); + + sprintf(buf, "%hhu", pkt[9]); + add_rule(server, "difficulty", buf, NO_FLAGS); + + /* unknown/undecoded fields after known flags removed */ + add_uchar_rule(server, "x4", (unsigned char)(pkt[4] &~(1+16))); + add_uchar_rule(server, "x5", (unsigned char)(pkt[5] &~(4+16))); + add_uchar_rule(server, "x6", (unsigned char)(pkt[6] &~1)); + + if (get_player_info && server->num_players) + { + server->next_player_info = 0; + send_player_request_packet(server); + return INPROGRESS; + } + + } + /* MP_PLAYERLIST_DATA */ + else if (rawpkt[1] == 0x73) + { + struct player *player; + struct player **last_player = &server->players; + + if (server->players != NULL) + { + return PKT_ERROR; + } + + pkt = &rawpkt[0x4]; + while (*pkt) + { + player = (struct player*)calloc(1, sizeof(struct player)); + player->name = strdup(pkt); + pkt += strlen(pkt) + 1; + *last_player = player; + last_player = &player->next; + } + server->next_player_info = NO_PLAYER_INFO; + } + else + { + fprintf(stderr, "unknown d3 packet\n"); + print_packet(server, rawpkt, pktlen); + } + + return DONE_FORCE; +} + diff --git a/reference/farcry/gamedignote.txt b/reference/farcry/gamedignote.txt index dfc5198..07262cf 100644 --- a/reference/farcry/gamedignote.txt +++ b/reference/farcry/gamedignote.txt @@ -1,2 +1,2 @@ -I was under the impression farcry uses ASE. -If anyone ever has issues, this is the doc for some old farcry query protocol. +I was under the impression farcry uses ASE. +If anyone ever has issues, this is the doc for some old farcry query protocol. diff --git a/reference/farcry/qstat.txt b/reference/farcry/qstat.txt index 58f244c..312a9d8 100644 --- a/reference/farcry/qstat.txt +++ b/reference/farcry/qstat.txt @@ -1,261 +1,261 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - unsigned char farcry_serverquery[] = { - 0x08,0x80 -}; - -{ - /* FARCRY PROTOCOL */ - FARCRY_SERVER, /* id */ - "FCS", /* type_prefix */ - "fcs", /* type_string */ - "-fcs", /* type_option */ - "FarCry", /* game_name */ - 0, /* master */ - FARCRY_DEFAULT_PORT, /* default_port */ - 0, /* port_offset */ - TF_QUERY_ARG, /* flags */ - "gametype", /* game_rule */ - "FARCRY", /* template_var */ - (char*)farcry_serverquery, /* status_packet */ - sizeof( savage_serverquery ) - 1, /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_farcry_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_farcry_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_farcry_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_farcry_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_farcry_packet, /* packet_func */ -}, - - - - -query_status_t send_farcry_request_packet(struct qserver *server) -{ - int len; - char *pkt; - - if (get_player_info) - { - pkt = server->type->player_packet; - len = server->type->player_len; - } - else - { - pkt = server->type->status_packet; - len = server->type->status_len; - } - - return send_packet(server, pkt, len); -} - - - - -query_status_t deal_with_farcry_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - char *s, *key, *value, *end; - - debug( 2, "deal_with_farcry_packet %p, %d", server, pktlen ); - - server->n_servers++; - if (NULL == server->server_name) - { - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - } - else - { - gettimeofday(&server->packet_time1, NULL); - } - - rawpkt[pktlen] = '\0'; - - end = s = rawpkt; - end += pktlen; - while (*s) - { - // Find the seperator - while (s <= end && *s != '\xFF') - { - s++; - } - - if (s >= end) - { - // Hit the end no more - break; - } - - // key start - key = ++s; - while (s < end && *s != '\xFE') - { - s++; - } - if (*s != '\xFE') - { - // malformed - break; - } - *s++ = '\0'; - // key end - // value start - value = s; - - while (s < end && *s != '\xFF') - { - s++; - } - - if (*s == '\xFF') - { - *s = '\0'; - } - //fprintf( stderr, "'%s' = '%s'\n", key, value ); - - // Decode current key par - if (0 == strcmp("cmax", key)) - { - // Max players - server->max_players = atoi(value); - } - else if (0 == strcmp("cnum", key)) - { - // Current players - server->num_players = atoi(value); - } - else if (0 == strcmp("bal", key)) - { - // Balance - add_rule(server, "Balance", value, NO_FLAGS); - } - else if (0 == strcmp("world", key)) - { - // Current map - server->map_name = strdup(value); - } - else if (0 == strcmp("gametype", key)) - { - // Game type - server->game = find_savage_game(value); - add_rule(server, server->type->game_rule, server->game, NO_FLAGS); - } - else if (0 == strcmp("pure", key)) - { - // Pure - add_rule(server, "Pure", value, NO_FLAGS); - } - else if (0 == strcmp("time", key)) - { - // Current game time - add_rule(server, "Time", value, NO_FLAGS); - } - else if (0 == strcmp("notes", key)) - { - // Notes - add_rule(server, "Notes", value, NO_FLAGS); - } - else if (0 == strcmp("needcmdr", key)) - { - // Need Commander - add_rule(server, "Need Commander", value, NO_FLAGS); - } - else if (0 == strcmp("name", key)) - { - // Server name - server->server_name = strdup(value); - } - else if (0 == strcmp("fw", key)) - { - // Firewalled - add_rule(server, "Firewalled", value, NO_FLAGS); - } - else if (0 == strcmp("players", key)) - { - - // Players names - int player_number = 0; - int team_number = 1; - char *team_name, *player_name, *n; - n = team_name = value; - - // team name - n++; - while (*n && *n != '\x0a') - { - n++; - } - - if (*n != '\x0a') - { - // Broken data - break; - } - *n = '\0'; - - player_name = ++n; - while (*n) - { - while (*n && *n != '\x0a') - { - n++; - } - - if (*n != '\x0a') - { - // Broken data - break; - } - *n = '\0'; - n++; - - if (0 == strncmp("Team ", player_name, 5)) - { - team_name = player_name; - team_number++; - } - else - { - if (0 != strlen(player_name)) - { - struct player *player = add_player(server, player_number); - if (NULL != player) - { - player->name = strdup(player_name); - player->team = team_number; - player->team_name = strdup(team_name); - } player_number++; - } - } - player_name = n; - } - } - - *s = '\xFF'; - } - - return DONE_FORCE; -} - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + unsigned char farcry_serverquery[] = { + 0x08,0x80 +}; + +{ + /* FARCRY PROTOCOL */ + FARCRY_SERVER, /* id */ + "FCS", /* type_prefix */ + "fcs", /* type_string */ + "-fcs", /* type_option */ + "FarCry", /* game_name */ + 0, /* master */ + FARCRY_DEFAULT_PORT, /* default_port */ + 0, /* port_offset */ + TF_QUERY_ARG, /* flags */ + "gametype", /* game_rule */ + "FARCRY", /* template_var */ + (char*)farcry_serverquery, /* status_packet */ + sizeof( savage_serverquery ) - 1, /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_farcry_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_farcry_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_farcry_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_farcry_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_farcry_packet, /* packet_func */ +}, + + + + +query_status_t send_farcry_request_packet(struct qserver *server) +{ + int len; + char *pkt; + + if (get_player_info) + { + pkt = server->type->player_packet; + len = server->type->player_len; + } + else + { + pkt = server->type->status_packet; + len = server->type->status_len; + } + + return send_packet(server, pkt, len); +} + + + + +query_status_t deal_with_farcry_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + char *s, *key, *value, *end; + + debug( 2, "deal_with_farcry_packet %p, %d", server, pktlen ); + + server->n_servers++; + if (NULL == server->server_name) + { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } + else + { + gettimeofday(&server->packet_time1, NULL); + } + + rawpkt[pktlen] = '\0'; + + end = s = rawpkt; + end += pktlen; + while (*s) + { + // Find the seperator + while (s <= end && *s != '\xFF') + { + s++; + } + + if (s >= end) + { + // Hit the end no more + break; + } + + // key start + key = ++s; + while (s < end && *s != '\xFE') + { + s++; + } + if (*s != '\xFE') + { + // malformed + break; + } + *s++ = '\0'; + // key end + // value start + value = s; + + while (s < end && *s != '\xFF') + { + s++; + } + + if (*s == '\xFF') + { + *s = '\0'; + } + //fprintf( stderr, "'%s' = '%s'\n", key, value ); + + // Decode current key par + if (0 == strcmp("cmax", key)) + { + // Max players + server->max_players = atoi(value); + } + else if (0 == strcmp("cnum", key)) + { + // Current players + server->num_players = atoi(value); + } + else if (0 == strcmp("bal", key)) + { + // Balance + add_rule(server, "Balance", value, NO_FLAGS); + } + else if (0 == strcmp("world", key)) + { + // Current map + server->map_name = strdup(value); + } + else if (0 == strcmp("gametype", key)) + { + // Game type + server->game = find_savage_game(value); + add_rule(server, server->type->game_rule, server->game, NO_FLAGS); + } + else if (0 == strcmp("pure", key)) + { + // Pure + add_rule(server, "Pure", value, NO_FLAGS); + } + else if (0 == strcmp("time", key)) + { + // Current game time + add_rule(server, "Time", value, NO_FLAGS); + } + else if (0 == strcmp("notes", key)) + { + // Notes + add_rule(server, "Notes", value, NO_FLAGS); + } + else if (0 == strcmp("needcmdr", key)) + { + // Need Commander + add_rule(server, "Need Commander", value, NO_FLAGS); + } + else if (0 == strcmp("name", key)) + { + // Server name + server->server_name = strdup(value); + } + else if (0 == strcmp("fw", key)) + { + // Firewalled + add_rule(server, "Firewalled", value, NO_FLAGS); + } + else if (0 == strcmp("players", key)) + { + + // Players names + int player_number = 0; + int team_number = 1; + char *team_name, *player_name, *n; + n = team_name = value; + + // team name + n++; + while (*n && *n != '\x0a') + { + n++; + } + + if (*n != '\x0a') + { + // Broken data + break; + } + *n = '\0'; + + player_name = ++n; + while (*n) + { + while (*n && *n != '\x0a') + { + n++; + } + + if (*n != '\x0a') + { + // Broken data + break; + } + *n = '\0'; + n++; + + if (0 == strncmp("Team ", player_name, 5)) + { + team_name = player_name; + team_number++; + } + else + { + if (0 != strlen(player_name)) + { + struct player *player = add_player(server, player_number); + if (NULL != player) + { + player->name = strdup(player_name); + player->team = team_number; + player->team_name = strdup(team_name); + } player_number++; + } + } + player_name = n; + } + } + + *s = '\xFF'; + } + + return DONE_FORCE; +} + diff --git a/reference/freelancer/gameq.txt b/reference/freelancer/gameq.txt index 1c72cb6..7804e0a 100644 --- a/reference/freelancer/gameq.txt +++ b/reference/freelancer/gameq.txt @@ -1,57 +1,57 @@ -. - * - * $Id: freelancer.php,v 1.2 2008/02/22 13:33:40 tombuskens Exp $ - */ - -[freelancer] -status = "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21" - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Freelancer protocol - * UNTESTED - * - * @author Tom Buskens - * @version $Revision: 1.2 $ - */ -class GameQ_Protocol_freelancer extends GameQ_Protocol -{ - /* - * status packet - */ - public function status() - { - // Server name length @ 3 - $this->p->skip(3); - $name_length = $this->p->readInt8() - 90; - - // Max players @ 20 - $this->p->skip(17); - $this->r->add('max_players', $this->p->readInt8() - 1); - // Num players @ 24 - $this->p->skip(3); - $this->r->add('num_players', $this->p->readInt8() - 1); - - // Servername @ 91 - $this->p->skip(66); - $this->r->add('servername', $this->p->read($name_length)); - } -} -?> +. + * + * $Id: freelancer.php,v 1.2 2008/02/22 13:33:40 tombuskens Exp $ + */ + +[freelancer] +status = "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21" + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Freelancer protocol + * UNTESTED + * + * @author Tom Buskens + * @version $Revision: 1.2 $ + */ +class GameQ_Protocol_freelancer extends GameQ_Protocol +{ + /* + * status packet + */ + public function status() + { + // Server name length @ 3 + $this->p->skip(3); + $name_length = $this->p->readInt8() - 90; + + // Max players @ 20 + $this->p->skip(17); + $this->r->add('max_players', $this->p->readInt8() - 1); + // Num players @ 24 + $this->p->skip(3); + $this->r->add('num_players', $this->p->readInt8() - 1); + + // Servername @ 91 + $this->p->skip(66); + $this->r->add('servername', $this->p->read($name_length)); + } +} +?> diff --git a/reference/freelancer/lgsl.txt b/reference/freelancer/lgsl.txt index 3e048c2..820e8b4 100644 --- a/reference/freelancer/lgsl.txt +++ b/reference/freelancer/lgsl.txt @@ -1,58 +1,58 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - -//------------------------------------------------------------------------------------------------------------+ -//------------------------------------------------------------------------------------------------------------+ - - function lgsl_query_14(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: http://flstat.cryosphere.co.uk/global-list.php - - fwrite($lgsl_fp, "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 4); // HEADER ( 00 03 F1 26 ) - $buffer = substr($buffer, 4); // NOT USED ( 87 + NAME LENGTH ) - $buffer = substr($buffer, 4); // NOT USED ( NAME END TO BUFFER END LENGTH ) - $buffer = substr($buffer, 4); // UNKNOWN ( 80 ) - - $server['s']['map'] = "freelancer"; - $server['s']['password'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1 ? 1 : 0; - $server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1; - $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1; - $buffer = substr($buffer, 4); // UNKNOWN ( 88 ) - $name_length = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); - $buffer = substr($buffer, 56); // UNKNOWN - $server['s']['name'] = lgsl_cut_byte($buffer, $name_length); - - lgsl_cut_string($buffer, 0, ":"); - lgsl_cut_string($buffer, 0, ":"); - lgsl_cut_string($buffer, 0, ":"); - lgsl_cut_string($buffer, 0, ":"); - lgsl_cut_string($buffer, 0, ":"); - - // WHATS LEFT IS THE MOTD - $server['e']['motd'] = substr($buffer, 0, -1); - - // REMOVE UTF-8 ENCODING NULLS - $server['s']['name'] = str_replace("\x00", "", $server['s']['name']); - $server['e']['motd'] = str_replace("\x00", "", $server['e']['motd']); - - // DOES NOT RETURN PLAYER INFORMATION - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + +//------------------------------------------------------------------------------------------------------------+ +//------------------------------------------------------------------------------------------------------------+ + + function lgsl_query_14(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: http://flstat.cryosphere.co.uk/global-list.php + + fwrite($lgsl_fp, "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 4); // HEADER ( 00 03 F1 26 ) + $buffer = substr($buffer, 4); // NOT USED ( 87 + NAME LENGTH ) + $buffer = substr($buffer, 4); // NOT USED ( NAME END TO BUFFER END LENGTH ) + $buffer = substr($buffer, 4); // UNKNOWN ( 80 ) + + $server['s']['map'] = "freelancer"; + $server['s']['password'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1 ? 1 : 0; + $server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1; + $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1; + $buffer = substr($buffer, 4); // UNKNOWN ( 88 ) + $name_length = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); + $buffer = substr($buffer, 56); // UNKNOWN + $server['s']['name'] = lgsl_cut_byte($buffer, $name_length); + + lgsl_cut_string($buffer, 0, ":"); + lgsl_cut_string($buffer, 0, ":"); + lgsl_cut_string($buffer, 0, ":"); + lgsl_cut_string($buffer, 0, ":"); + lgsl_cut_string($buffer, 0, ":"); + + // WHATS LEFT IS THE MOTD + $server['e']['motd'] = substr($buffer, 0, -1); + + // REMOVE UTF-8 ENCODING NULLS + $server['s']['name'] = str_replace("\x00", "", $server['s']['name']); + $server['e']['motd'] = str_replace("\x00", "", $server['e']['motd']); + + // DOES NOT RETURN PLAYER INFORMATION + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/ghostrecon/gameq.txt b/reference/ghostrecon/gameq.txt index 4be0de1..0652b67 100644 --- a/reference/ghostrecon/gameq.txt +++ b/reference/ghostrecon/gameq.txt @@ -1,64 +1,64 @@ -. - * - * $Id: ghostrecon.php,v 1.1 2007/07/02 10:14:32 tombuskens Exp $ - */ - -[ghostrecon] -status = "\xc0\xde\xf1\x11\x42\x06\x00\xf5\x03\x00\x78\x30\x63" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Ghost Recon protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_ghostrecon extends GameQ_Protocol -{ - /* - * Status - */ - public function status() - { - // Unknown - $this->p->skip(25); - - $this->r->add('servername', $this->readGhostString()); - $this->r->add('map', $this->readGhostString()); - $this->r->add('mission', $this->readGhostString()); - $this->r->add('gametype', $this->readGhostString()); - } - - /** - * Read a Ghost Recon string - * - * @return string The string - */ - private function readGhostString() - { - if ($this->p->getLength() < 4) return ''; - $this->p->skip(4); - - return $this->p->readString(); - - } -} -?> +. + * + * $Id: ghostrecon.php,v 1.1 2007/07/02 10:14:32 tombuskens Exp $ + */ + +[ghostrecon] +status = "\xc0\xde\xf1\x11\x42\x06\x00\xf5\x03\x00\x78\x30\x63" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Ghost Recon protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_ghostrecon extends GameQ_Protocol +{ + /* + * Status + */ + public function status() + { + // Unknown + $this->p->skip(25); + + $this->r->add('servername', $this->readGhostString()); + $this->r->add('map', $this->readGhostString()); + $this->r->add('mission', $this->readGhostString()); + $this->r->add('gametype', $this->readGhostString()); + } + + /** + * Read a Ghost Recon string + * + * @return string The string + */ + private function readGhostString() + { + if ($this->p->getLength() < 4) return ''; + $this->p->skip(4); + + return $this->p->readString(); + + } +} +?> diff --git a/reference/ghostrecon/lgsl.txt b/reference/ghostrecon/lgsl.txt index afb3aab..855487b 100644 --- a/reference/ghostrecon/lgsl.txt +++ b/reference/ghostrecon/lgsl.txt @@ -1,133 +1,133 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - - function lgsl_query_19(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ - - fwrite($lgsl_fp, "\xC0\xDE\xF1\x11\x42\x06\x00\xF5\x03\x21\x21\x21\x21"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 25); // REMOVE HEADER - - $server['s']['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - $server['s']['map'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - $server['e']['nextmap'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - $server['e']['gametype'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - - $buffer = substr($buffer, 1); - - $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 4)); - $server['s']['players'] = ord(lgsl_cut_byte($buffer, 4)); - -//---------------------------------------------------------+ - - for ($player_key=0; $player_key<$server['s']['players']; $player_key++) - { - $server['p'][$player_key]['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 17); - - $server['e']['version'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - $server['e']['mods'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['status'] = ord(lgsl_cut_byte($buffer, 4)); - $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 4)); - $server['e']['motd'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - $server['e']['respawns'] = ord(lgsl_cut_byte($buffer, 4)); - $server['e']['time_limit'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['voting'] = ord(lgsl_cut_byte($buffer, 4)); - - $buffer = substr($buffer, 2); - -//---------------------------------------------------------+ - - for ($player_key=0; $player_key<$server['s']['players']; $player_key++) - { - $server['p'][$player_key]['team'] = ord(lgsl_cut_byte($buffer, 4)); - - $unknown = ord(lgsl_cut_byte($buffer, 1)); - } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 7); - - $server['e']['platoon_1_color'] = ord(lgsl_cut_byte($buffer, 8)); - $server['e']['platoon_2_color'] = ord(lgsl_cut_byte($buffer, 8)); - $server['e']['platoon_3_color'] = ord(lgsl_cut_byte($buffer, 8)); - $server['e']['platoon_4_color'] = ord(lgsl_cut_byte($buffer, 8)); - $server['e']['timer_on'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['timer_time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['time_debriefing'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['time_respawn_min'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['time_respawn_max'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['time_respawn_safe'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['difficulty'] = ord(lgsl_cut_byte($buffer, 4)); - $server['e']['respawn_total'] = ord(lgsl_cut_byte($buffer, 4)); - $server['e']['random_insertions'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['arcademode'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['ai_backup'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['random_teams'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['time_starting'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); - $server['e']['identify_friends'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['identify_threats'] = ord(lgsl_cut_byte($buffer, 1)); - - $buffer = substr($buffer, 5); - - $server['e']['restrictions'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); - -//---------------------------------------------------------+ - - switch ($server['e']['status']) - { - case 3: $server['e']['status'] = "Joining"; break; - case 4: $server['e']['status'] = "Joining"; break; - case 5: $server['e']['status'] = "Joining"; break; - } - - switch ($server['e']['gamemode']) - { - case 2: $server['e']['gamemode'] = "Co-Op"; break; - case 3: $server['e']['gamemode'] = "Solo"; break; - case 4: $server['e']['gamemode'] = "Team"; break; - } - - switch ($server['e']['respawns']) - { - case 0: $server['e']['respawns'] = "None"; break; - case 1: $server['e']['respawns'] = "Individual"; break; - case 2: $server['e']['respawns'] = "Team"; break; - case 3: $server['e']['respawns'] = "Infinite"; break; - } - - switch ($server['e']['difficulty']) - { - case 0: $server['e']['difficulty'] = "Recruit"; break; - case 1: $server['e']['difficulty'] = "Veteran"; break; - case 2: $server['e']['difficulty'] = "Elite"; break; - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + + function lgsl_query_19(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ + + fwrite($lgsl_fp, "\xC0\xDE\xF1\x11\x42\x06\x00\xF5\x03\x21\x21\x21\x21"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 25); // REMOVE HEADER + + $server['s']['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + $server['s']['map'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + $server['e']['nextmap'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + $server['e']['gametype'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + + $buffer = substr($buffer, 1); + + $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 4)); + $server['s']['players'] = ord(lgsl_cut_byte($buffer, 4)); + +//---------------------------------------------------------+ + + for ($player_key=0; $player_key<$server['s']['players']; $player_key++) + { + $server['p'][$player_key]['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 17); + + $server['e']['version'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + $server['e']['mods'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['status'] = ord(lgsl_cut_byte($buffer, 4)); + $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 4)); + $server['e']['motd'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + $server['e']['respawns'] = ord(lgsl_cut_byte($buffer, 4)); + $server['e']['time_limit'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['voting'] = ord(lgsl_cut_byte($buffer, 4)); + + $buffer = substr($buffer, 2); + +//---------------------------------------------------------+ + + for ($player_key=0; $player_key<$server['s']['players']; $player_key++) + { + $server['p'][$player_key]['team'] = ord(lgsl_cut_byte($buffer, 4)); + + $unknown = ord(lgsl_cut_byte($buffer, 1)); + } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 7); + + $server['e']['platoon_1_color'] = ord(lgsl_cut_byte($buffer, 8)); + $server['e']['platoon_2_color'] = ord(lgsl_cut_byte($buffer, 8)); + $server['e']['platoon_3_color'] = ord(lgsl_cut_byte($buffer, 8)); + $server['e']['platoon_4_color'] = ord(lgsl_cut_byte($buffer, 8)); + $server['e']['timer_on'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['timer_time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['time_debriefing'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['time_respawn_min'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['time_respawn_max'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['time_respawn_safe'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['difficulty'] = ord(lgsl_cut_byte($buffer, 4)); + $server['e']['respawn_total'] = ord(lgsl_cut_byte($buffer, 4)); + $server['e']['random_insertions'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['arcademode'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['ai_backup'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['random_teams'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['time_starting'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); + $server['e']['identify_friends'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['identify_threats'] = ord(lgsl_cut_byte($buffer, 1)); + + $buffer = substr($buffer, 5); + + $server['e']['restrictions'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); + +//---------------------------------------------------------+ + + switch ($server['e']['status']) + { + case 3: $server['e']['status'] = "Joining"; break; + case 4: $server['e']['status'] = "Joining"; break; + case 5: $server['e']['status'] = "Joining"; break; + } + + switch ($server['e']['gamemode']) + { + case 2: $server['e']['gamemode'] = "Co-Op"; break; + case 3: $server['e']['gamemode'] = "Solo"; break; + case 4: $server['e']['gamemode'] = "Team"; break; + } + + switch ($server['e']['respawns']) + { + case 0: $server['e']['respawns'] = "None"; break; + case 1: $server['e']['respawns'] = "Individual"; break; + case 2: $server['e']['respawns'] = "Team"; break; + case 3: $server['e']['respawns'] = "Infinite"; break; + } + + switch ($server['e']['difficulty']) + { + case 0: $server['e']['difficulty'] = "Recruit"; break; + case 1: $server['e']['difficulty'] = "Veteran"; break; + case 2: $server['e']['difficulty'] = "Elite"; break; + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/ghostrecon/qstat.txt b/reference/ghostrecon/qstat.txt index d0fbfd8..6b3d538 100644 --- a/reference/ghostrecon/qstat.txt +++ b/reference/ghostrecon/qstat.txt @@ -1,694 +1,694 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - -unsigned char ghostrecon_serverquery[] = { - 0xc0,0xde,0xf1,0x11, /* const ? header */ - 0x42, /* start flag */ - 0x03,0x00, /* data len */ - 0xe9,0x03,0x00 /* server request ?? */ -}; - -unsigned char ghostrecon_playerquery[] = { - 0xc0,0xde,0xf1,0x11, /* const ? header */ - 0x42, /* start flag */ - 0x06,0x00, /* data len */ - 0xf5,0x03,0x00,0x78,0x30,0x63 /* player request ?? may be flag 0xf5; len 0x03,0x00; data 0x78, 0x30, 0x63 */ -}; - - - { - /* GHOSTRECON PROTOCOL */ - GHOSTRECON_SERVER, /* id */ - "GRS", /* type_prefix */ - "grs", /* type_string */ - "-grs", /* type_option */ - "Ghost Recon", /* game_name */ - 0, /* master */ - GHOSTRECON_PLAYER_DEFAULT_PORT, /* default_port */ - 2, /* port_offset */ - TF_QUERY_ARG, /* flags */ - "gametype", /* game_rule */ - "GHOSTRECON", /* template_var */ - (char*) &ghostrecon_playerquery, /* status_packet */ - sizeof( ghostrecon_playerquery), /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_ghostrecon_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_ghostrecon_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_ghostrecon_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_qserver_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_ghostrecon_packet, /* packet_func */ -}, - - - - -static const char GrPacketHead[] = -{ - '\xc0', '\xde', '\xf1', '\x11' -}; -static const char PacketStart = '\x42'; -static char Dat2Reply1_2_10[] = -{ - '\xf4', '\x03', '\x14', '\x02', '\x0a', '\x41', '\x02', '\x0a', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63' -}; -static char Dat2Reply1_3[] = -{ - '\xf4', '\x03', '\x14', '\x03', '\x05', '\x41', '\x03', '\x05', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63' -}; -static char Dat2Reply1_4[] = -{ - '\xf4', '\x03', '\x14', '\x04', '\x00', '\x41', '\x04', '\x00', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63' -}; -//static char HDat2[]={'\xea','\x03','\x02','\x00','\x14'}; - -#define SHORT_GR_LEN 75 -#define LONG_GR_LEN 500 -#define UNKNOWN_VERSION 0 -#define VERSION_1_2_10 1 -#define VERSION_1_3 2 -#define VERSION_1_4 3 - -query_status_t deal_with_ghostrecon_packet(struct qserver *server, char *pkt, int pktlen) -{ - char str[256], *start, *end, StartFlag, *lpszIgnoreServerPlayer; - char *lpszMission; - unsigned int iIgnoreServerPlayer, iDedicatedServer, iUseStartTimer; - unsigned short GrPayloadLen; - int i; - struct player *player; - int iLen, iTemp; - short sLen; - int iSecsPlayed; - long iSpawnType; - int ServerVersion = UNKNOWN_VERSION; - float flStartTimerSetPoint; - - debug( 2, "deal_with_ghostrecon_packet %p, %d", server, pktlen ); - - start = pkt; - end = &pkt[pktlen]; - pkt[pktlen] = '\0'; - - /* - This function walks a packet that is recieved from a ghost recon server - default from port 2348. It does quite a few - sanity checks along the way as the structure is not documented. The packet is mostly binary in nature with many string - fields being variable in length, ie the length is listed foloowed by that many bytes. There are two structure arrays - that have an array size followed by structure size * number of elements (player name and player data). This routine - walks this packet and increments a pointer "pkt" to extract the info. - */ - - - if (server->server_name == NULL) - { - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - } - - /* sanity check against packet */ - if (memcmp(pkt, GrPacketHead, sizeof(GrPacketHead)) != 0) - { - server->server_name = strdup("Unknown Packet Header"); - return PKT_ERROR; - } - - pkt += sizeof(GrPacketHead); - StartFlag = pkt[0]; - pkt += 1; - if (StartFlag != 0x42) - { - server->server_name = strdup("Unknown Start Flag"); - return PKT_ERROR; - } - - /* compare packet length recieved to included size - header info */ - sLen = swap_short_from_little(pkt); - pkt += 2; - GrPayloadLen = pktlen - sizeof(GrPacketHead) - 3; - // 3 = size slen + size start flag - - if (sLen != GrPayloadLen) - { - server->server_name = strdup("Packet Size Mismatch"); - return PKT_ERROR; - } - - /* - Will likely need to verify and add to this "if" construct with every patch / add-on. - */ - if (memcmp(pkt, Dat2Reply1_2_10, sizeof(Dat2Reply1_2_10)) == 0) - { - ServerVersion = VERSION_1_2_10; - } - else if (memcmp(pkt, Dat2Reply1_3, sizeof(Dat2Reply1_3)) == 0) - { - ServerVersion = VERSION_1_3; - } - else if (memcmp(pkt, Dat2Reply1_4, sizeof(Dat2Reply1_4)) == 0) - { - ServerVersion = VERSION_1_4; - } - - if (ServerVersion == UNKNOWN_VERSION) - { - server->server_name = strdup("Unknown GR Version"); - return PKT_ERROR; - } - - switch (ServerVersion) - { - case VERSION_1_2_10: - strcpy(str, "1.2.10"); - pkt += sizeof(Dat2Reply1_2_10); - break; - - case VERSION_1_3: - strcpy(str, "1.3"); - pkt += sizeof(Dat2Reply1_3); - break; - - case VERSION_1_4: - strcpy(str, "1.4"); - pkt += sizeof(Dat2Reply1_4); - break; - - } - add_rule(server, "patch", str, NO_FLAGS); - - /* have player packet */ - - // Ghost recon has one of the player slots filled up with the server program itself. By default we will - // drop the first player listed. This causes a bit of a mess here and below but makes for the best display - // a user can specify -grs,ignoreserverplayer=no to override this behaviour. - - lpszIgnoreServerPlayer = get_param_value(server, "ignoreserverplayer", "yes"); - for (i = 0; i < 4; i++) - { - str[i] = tolower(lpszIgnoreServerPlayer[i]); - } - if (strcmp(str, "yes") == 0) - { - iIgnoreServerPlayer = 1; - } - else - { - iIgnoreServerPlayer = 0; - } - - pkt += 4; /* unknown */ - - - // this is the first of many variable strings. get the length, - // increment pointer over length, check for sanity, - // get the string, increment the pointer over string (using length) - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > SHORT_GR_LEN)) - { - server->server_name = strdup("Server Name too Long"); - return PKT_ERROR; - } - server->server_name = strndup(pkt, iLen); - pkt += iLen; - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > SHORT_GR_LEN)) - { - add_rule(server, "error", "Map Name too Long", NO_FLAGS); - return PKT_ERROR; - } - server->map_name = strndup(pkt, iLen); - pkt += iLen; - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > SHORT_GR_LEN)) - { - add_rule(server, "error", "Mission Name too Long", NO_FLAGS); - return PKT_ERROR; - } - /* mission does not make sense unless a coop game type. Since - we dont know that now, we will save the mission and set - the rule and free memory below when we know game type */ - lpszMission = strndup(pkt, iLen); - pkt += iLen; - - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > SHORT_GR_LEN)) - { - add_rule(server, "error", "Mission Type too Long", NO_FLAGS); - return PKT_ERROR; - } - add_nrule(server, "missiontype", pkt, iLen); - pkt += iLen; - - if (pkt[1]) - { - add_rule(server, "password", "Yes", NO_FLAGS); - } - else - { - add_rule(server, "password", "No", NO_FLAGS); - } - pkt += 2; - - server->max_players = swap_long_from_little(pkt); - pkt += 4; - if (server->max_players > 36) - { - add_rule(server, "error", "Max players more then 36", NO_FLAGS); - return PKT_ERROR; - } - - server->num_players = swap_long_from_little(pkt); - pkt += 4; - if (server->num_players > server->max_players) - { - add_rule(server, "error", "More then MAX Players", NO_FLAGS); - return PKT_ERROR; - } - - if (iIgnoreServerPlayer) - // skip past first player - { - server->num_players--; - server->max_players--; - iLen = swap_long_from_little(pkt); - pkt += 4; - pkt += iLen; - } - - for (i = 0; i < server->num_players; i++) - // read each player name - { - iLen = swap_long_from_little(pkt); - pkt += 4; - - player = (struct player*)calloc(1, sizeof(struct player)); - - if ((iLen < 1) || (iLen > SHORT_GR_LEN)) - { - add_rule(server, "error", "Player Name too Long", NO_FLAGS); - return PKT_ERROR; - } - player->name = strndup(pkt, iLen); - pkt += iLen; /* player name */ - player->team = i; // tag so we can find this record when we have player dat. - player->team_name = "Unassigned"; - player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; - player->frags = 0; - - player->next = server->players; - server->players = player; - } - - pkt += 17; - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > SHORT_GR_LEN)) - { - add_rule(server, "error", "Version too Long", NO_FLAGS); - return PKT_ERROR; - } - strncpy(str, pkt, iLen); - add_rule(server, "version", str, NO_FLAGS); - pkt += iLen; /* version */ - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > LONG_GR_LEN)) - { - add_rule(server, "error", "Mods too Long", NO_FLAGS); - return PKT_ERROR; - } - server->game = strndup(pkt, iLen); - - for (i = 0; i < (int)strlen(server->game) - 5; i++) - // clean the "/mods/" part from every entry - { - if (memcmp(&server->game[i], "\\mods\\", 6) == 0) - { - server->game[i] = ' '; - strcpy(&server->game[i + 1], &server->game[i + 6]); - } - } - add_rule(server, "game", server->game, NO_FLAGS); - - pkt += iLen; /* mods */ - - iDedicatedServer = pkt[0]; - if (iDedicatedServer) - { - add_rule(server, "dedicated", "Yes", NO_FLAGS); - } - else - { - add_rule(server, "dedicated", "No", NO_FLAGS); - } - - pkt += 1; /* unknown */ - - iSecsPlayed = swap_float_from_little(pkt); - - add_rule(server, "timeplayed", play_time(iSecsPlayed, 2), NO_FLAGS); - - pkt += 4; /* time played */ - - switch (pkt[0]) - { - case 3: - strcpy(str, "Joining"); - break; - case 4: - strcpy(str, "Playing"); - break; - case 5: - strcpy(str, "Debrief"); - break; - default: - strcpy(str, "Undefined"); - } - add_rule(server, "status", str, NO_FLAGS); - - pkt += 1; - - pkt += 3; /* unknown */ - - - switch (pkt[0]) - { - case 2: - strcpy(str, "COOP"); - break; - case 3: - strcpy(str, "SOLO"); - break; - case 4: - strcpy(str, "TEAM"); - break; - default: - sprintf(str, "UNKOWN %u", pkt[0]); - break; - } - - add_rule(server, "gamemode", str, NO_FLAGS); - - if (pkt[0] == 2) - { - add_rule(server, "mission", lpszMission, NO_FLAGS); - } - else - { - add_rule(server, "mission", "No Mission", NO_FLAGS); - } - - free(lpszMission); - - pkt += 1; /* Game Mode */ - - pkt += 3; /* unknown */ - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > LONG_GR_LEN)) - { - add_rule(server, "error", "MOTD too Long", NO_FLAGS); - return PKT_ERROR; - } - strncpy(str, pkt, sizeof(str)); - str[sizeof(str) - 1] = 0; - add_rule(server, "motd", str, NO_FLAGS); - pkt += iLen; /* MOTD */ - - iSpawnType = swap_long_from_little(pkt); - - switch (iSpawnType) - { - case 0: - strcpy(str, "None"); - break; - case 1: - strcpy(str, "Individual"); - break; - case 2: - strcpy(str, "Team"); - break; - case 3: - strcpy(str, "Infinite"); - break; - default: - strcpy(str, "Unknown"); - } - - add_rule(server, "spawntype", str, NO_FLAGS); - pkt += 4; /* spawn type */ - - iTemp = swap_float_from_little(pkt); - add_rule(server, "gametime", play_time(iTemp, 2), NO_FLAGS); - - iTemp = iTemp - iSecsPlayed; - - if (iTemp <= 0) - { - iTemp = 0; - } - add_rule(server, "remainingtime", play_time(iTemp, 2), NO_FLAGS); - pkt += 4; /* Game time */ - - - iTemp = swap_long_from_little(pkt); - if (iIgnoreServerPlayer) - { - iTemp--; - } - if (iTemp != server->num_players) - { - add_rule(server, "error", "Number of Players Mismatch", NO_FLAGS); - } - - - pkt += 4; /* player count 2 */ - - if (iIgnoreServerPlayer) - { - pkt += 5; // skip first player data - } - - for (i = 0; i < server->num_players; i++) - // for each player get binary data - { - player = server->players; - // first we must find the player - lets look for the tag - while (player && (player->team != i)) - { - player = player->next; - } - /* get to player - linked list is in reverse order */ - - if (player) - { - player->team = pkt[2]; - switch (player->team) - { - case 1: - player->team_name = "Red"; - break; - case 2: - player->team_name = "Blue"; - break; - case 3: - player->team_name = "Yellow"; - break; - case 4: - player->team_name = "Green"; - break; - case 5: - player->team_name = "Unassigned"; - break; - default: - player->team_name = "Not Known"; - break; - } - player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; - player->deaths = pkt[1]; - } - pkt += 5; /* player data*/ - } - - for (i = 0; i < 5; i++) - { - pkt += 8; /* team data who knows what they have in here */ - } - - pkt += 1; - iUseStartTimer = pkt[0]; // UseStartTimer - - pkt += 1; - - iTemp = flStartTimerSetPoint = swap_float_from_little(pkt); - // Start Timer Set Point - pkt += 4; - - if (iUseStartTimer) - { - add_rule(server, "usestarttime", "Yes", NO_FLAGS); - add_rule(server, "starttimeset", play_time(iTemp, 2), NO_FLAGS); - } - else - { - add_rule(server, "usestarttime", "No", NO_FLAGS); - add_rule(server, "starttimeset", play_time(0, 2), NO_FLAGS); - } - - if ((ServerVersion == VERSION_1_3) || // stuff added in patch 1.3 - (ServerVersion == VERSION_1_4)) - { - iTemp = swap_float_from_little(pkt); // Debrief Time - add_rule(server, "debrieftime", play_time(iTemp, 2), NO_FLAGS); - pkt += 4; - - iTemp = swap_float_from_little(pkt); // Respawn Min - add_rule(server, "respawnmin", play_time(iTemp, 2), NO_FLAGS); - pkt += 4; - - iTemp = swap_float_from_little(pkt); // Respawn Max - add_rule(server, "respawnmax", play_time(iTemp, 2), NO_FLAGS); - pkt += 4; - - iTemp = swap_float_from_little(pkt); // Respawn Invulnerable - add_rule(server, "respawnsafe", play_time(iTemp, 2), NO_FLAGS); - pkt += 4; - } - else - { - add_rule(server, "debrieftime", "Undefined", NO_FLAGS); - add_rule(server, "respawnmin", "Undefined", NO_FLAGS); - add_rule(server, "respawnmax", "Undefined", NO_FLAGS); - add_rule(server, "respawnsafe", "Undefined", NO_FLAGS); - - } - - - pkt += 4; // 4 - iTemp = pkt[0]; // Spawn Count - - if ((iSpawnType == 1) || (iSpawnType == 2)) - /* Individual or team */ - { - sprintf(str, "%u", iTemp); - } - else - /* else not used */ - { - sprintf(str, "%u", 0); - } - add_rule(server, "spawncount", str, NO_FLAGS); - pkt += 1; // 5 - - pkt += 4; // 9 - - iTemp = pkt[0]; // Allow Observers - if (iTemp) - { - strcpy(str, "Yes"); - } - else - /* else not used */ - { - strcpy(str, "No"); - } - add_rule(server, "allowobservers", str, NO_FLAGS); - pkt += 1; // 10 - - pkt += 3; // 13 - - // pkt += 13; - - if (iUseStartTimer) - { - iTemp = swap_float_from_little(pkt); // Start Timer Count - add_rule(server, "startwait", play_time(iTemp, 2), NO_FLAGS); - } - else - { - add_rule(server, "startwait", play_time(0, 2), NO_FLAGS); - } - pkt += 4; //17 - - iTemp = pkt[0]; // IFF - switch (iTemp) - { - case 0: - strcpy(str, "None"); - break; - case 1: - strcpy(str, "Reticule"); - break; - case 2: - strcpy(str, "Names"); - break; - default: - strcpy(str, "Unknown"); - break; - } - add_rule(server, "iff", str, NO_FLAGS); - pkt += 1; // 18 - - iTemp = pkt[0]; // Threat Indicator - if (iTemp) - { - add_rule(server, "ti", "ON ", NO_FLAGS); - } - else - { - add_rule(server, "ti", "OFF", NO_FLAGS); - } - - pkt += 1; // 19 - - pkt += 5; // 24 - - - iLen = swap_long_from_little(pkt); - pkt += 4; - if ((iLen < 1) || (iLen > SHORT_GR_LEN)) - { - add_rule(server, "error", "Restrictions too Long", NO_FLAGS); - return PKT_ERROR; - } - add_rule(server, "restrict", pkt, NO_FLAGS); - pkt += iLen; /* restrictions */ - - pkt += 23; - - /* - if ( ghostrecon_debug) print_packet( pkt, GrPayloadLen); - */ - - return DONE_FORCE; -} +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + +unsigned char ghostrecon_serverquery[] = { + 0xc0,0xde,0xf1,0x11, /* const ? header */ + 0x42, /* start flag */ + 0x03,0x00, /* data len */ + 0xe9,0x03,0x00 /* server request ?? */ +}; + +unsigned char ghostrecon_playerquery[] = { + 0xc0,0xde,0xf1,0x11, /* const ? header */ + 0x42, /* start flag */ + 0x06,0x00, /* data len */ + 0xf5,0x03,0x00,0x78,0x30,0x63 /* player request ?? may be flag 0xf5; len 0x03,0x00; data 0x78, 0x30, 0x63 */ +}; + + + { + /* GHOSTRECON PROTOCOL */ + GHOSTRECON_SERVER, /* id */ + "GRS", /* type_prefix */ + "grs", /* type_string */ + "-grs", /* type_option */ + "Ghost Recon", /* game_name */ + 0, /* master */ + GHOSTRECON_PLAYER_DEFAULT_PORT, /* default_port */ + 2, /* port_offset */ + TF_QUERY_ARG, /* flags */ + "gametype", /* game_rule */ + "GHOSTRECON", /* template_var */ + (char*) &ghostrecon_playerquery, /* status_packet */ + sizeof( ghostrecon_playerquery), /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_ghostrecon_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_ghostrecon_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_ghostrecon_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_qserver_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_ghostrecon_packet, /* packet_func */ +}, + + + + +static const char GrPacketHead[] = +{ + '\xc0', '\xde', '\xf1', '\x11' +}; +static const char PacketStart = '\x42'; +static char Dat2Reply1_2_10[] = +{ + '\xf4', '\x03', '\x14', '\x02', '\x0a', '\x41', '\x02', '\x0a', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63' +}; +static char Dat2Reply1_3[] = +{ + '\xf4', '\x03', '\x14', '\x03', '\x05', '\x41', '\x03', '\x05', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63' +}; +static char Dat2Reply1_4[] = +{ + '\xf4', '\x03', '\x14', '\x04', '\x00', '\x41', '\x04', '\x00', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63' +}; +//static char HDat2[]={'\xea','\x03','\x02','\x00','\x14'}; + +#define SHORT_GR_LEN 75 +#define LONG_GR_LEN 500 +#define UNKNOWN_VERSION 0 +#define VERSION_1_2_10 1 +#define VERSION_1_3 2 +#define VERSION_1_4 3 + +query_status_t deal_with_ghostrecon_packet(struct qserver *server, char *pkt, int pktlen) +{ + char str[256], *start, *end, StartFlag, *lpszIgnoreServerPlayer; + char *lpszMission; + unsigned int iIgnoreServerPlayer, iDedicatedServer, iUseStartTimer; + unsigned short GrPayloadLen; + int i; + struct player *player; + int iLen, iTemp; + short sLen; + int iSecsPlayed; + long iSpawnType; + int ServerVersion = UNKNOWN_VERSION; + float flStartTimerSetPoint; + + debug( 2, "deal_with_ghostrecon_packet %p, %d", server, pktlen ); + + start = pkt; + end = &pkt[pktlen]; + pkt[pktlen] = '\0'; + + /* + This function walks a packet that is recieved from a ghost recon server - default from port 2348. It does quite a few + sanity checks along the way as the structure is not documented. The packet is mostly binary in nature with many string + fields being variable in length, ie the length is listed foloowed by that many bytes. There are two structure arrays + that have an array size followed by structure size * number of elements (player name and player data). This routine + walks this packet and increments a pointer "pkt" to extract the info. + */ + + + if (server->server_name == NULL) + { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } + + /* sanity check against packet */ + if (memcmp(pkt, GrPacketHead, sizeof(GrPacketHead)) != 0) + { + server->server_name = strdup("Unknown Packet Header"); + return PKT_ERROR; + } + + pkt += sizeof(GrPacketHead); + StartFlag = pkt[0]; + pkt += 1; + if (StartFlag != 0x42) + { + server->server_name = strdup("Unknown Start Flag"); + return PKT_ERROR; + } + + /* compare packet length recieved to included size - header info */ + sLen = swap_short_from_little(pkt); + pkt += 2; + GrPayloadLen = pktlen - sizeof(GrPacketHead) - 3; + // 3 = size slen + size start flag + + if (sLen != GrPayloadLen) + { + server->server_name = strdup("Packet Size Mismatch"); + return PKT_ERROR; + } + + /* + Will likely need to verify and add to this "if" construct with every patch / add-on. + */ + if (memcmp(pkt, Dat2Reply1_2_10, sizeof(Dat2Reply1_2_10)) == 0) + { + ServerVersion = VERSION_1_2_10; + } + else if (memcmp(pkt, Dat2Reply1_3, sizeof(Dat2Reply1_3)) == 0) + { + ServerVersion = VERSION_1_3; + } + else if (memcmp(pkt, Dat2Reply1_4, sizeof(Dat2Reply1_4)) == 0) + { + ServerVersion = VERSION_1_4; + } + + if (ServerVersion == UNKNOWN_VERSION) + { + server->server_name = strdup("Unknown GR Version"); + return PKT_ERROR; + } + + switch (ServerVersion) + { + case VERSION_1_2_10: + strcpy(str, "1.2.10"); + pkt += sizeof(Dat2Reply1_2_10); + break; + + case VERSION_1_3: + strcpy(str, "1.3"); + pkt += sizeof(Dat2Reply1_3); + break; + + case VERSION_1_4: + strcpy(str, "1.4"); + pkt += sizeof(Dat2Reply1_4); + break; + + } + add_rule(server, "patch", str, NO_FLAGS); + + /* have player packet */ + + // Ghost recon has one of the player slots filled up with the server program itself. By default we will + // drop the first player listed. This causes a bit of a mess here and below but makes for the best display + // a user can specify -grs,ignoreserverplayer=no to override this behaviour. + + lpszIgnoreServerPlayer = get_param_value(server, "ignoreserverplayer", "yes"); + for (i = 0; i < 4; i++) + { + str[i] = tolower(lpszIgnoreServerPlayer[i]); + } + if (strcmp(str, "yes") == 0) + { + iIgnoreServerPlayer = 1; + } + else + { + iIgnoreServerPlayer = 0; + } + + pkt += 4; /* unknown */ + + + // this is the first of many variable strings. get the length, + // increment pointer over length, check for sanity, + // get the string, increment the pointer over string (using length) + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > SHORT_GR_LEN)) + { + server->server_name = strdup("Server Name too Long"); + return PKT_ERROR; + } + server->server_name = strndup(pkt, iLen); + pkt += iLen; + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > SHORT_GR_LEN)) + { + add_rule(server, "error", "Map Name too Long", NO_FLAGS); + return PKT_ERROR; + } + server->map_name = strndup(pkt, iLen); + pkt += iLen; + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > SHORT_GR_LEN)) + { + add_rule(server, "error", "Mission Name too Long", NO_FLAGS); + return PKT_ERROR; + } + /* mission does not make sense unless a coop game type. Since + we dont know that now, we will save the mission and set + the rule and free memory below when we know game type */ + lpszMission = strndup(pkt, iLen); + pkt += iLen; + + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > SHORT_GR_LEN)) + { + add_rule(server, "error", "Mission Type too Long", NO_FLAGS); + return PKT_ERROR; + } + add_nrule(server, "missiontype", pkt, iLen); + pkt += iLen; + + if (pkt[1]) + { + add_rule(server, "password", "Yes", NO_FLAGS); + } + else + { + add_rule(server, "password", "No", NO_FLAGS); + } + pkt += 2; + + server->max_players = swap_long_from_little(pkt); + pkt += 4; + if (server->max_players > 36) + { + add_rule(server, "error", "Max players more then 36", NO_FLAGS); + return PKT_ERROR; + } + + server->num_players = swap_long_from_little(pkt); + pkt += 4; + if (server->num_players > server->max_players) + { + add_rule(server, "error", "More then MAX Players", NO_FLAGS); + return PKT_ERROR; + } + + if (iIgnoreServerPlayer) + // skip past first player + { + server->num_players--; + server->max_players--; + iLen = swap_long_from_little(pkt); + pkt += 4; + pkt += iLen; + } + + for (i = 0; i < server->num_players; i++) + // read each player name + { + iLen = swap_long_from_little(pkt); + pkt += 4; + + player = (struct player*)calloc(1, sizeof(struct player)); + + if ((iLen < 1) || (iLen > SHORT_GR_LEN)) + { + add_rule(server, "error", "Player Name too Long", NO_FLAGS); + return PKT_ERROR; + } + player->name = strndup(pkt, iLen); + pkt += iLen; /* player name */ + player->team = i; // tag so we can find this record when we have player dat. + player->team_name = "Unassigned"; + player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; + player->frags = 0; + + player->next = server->players; + server->players = player; + } + + pkt += 17; + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > SHORT_GR_LEN)) + { + add_rule(server, "error", "Version too Long", NO_FLAGS); + return PKT_ERROR; + } + strncpy(str, pkt, iLen); + add_rule(server, "version", str, NO_FLAGS); + pkt += iLen; /* version */ + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > LONG_GR_LEN)) + { + add_rule(server, "error", "Mods too Long", NO_FLAGS); + return PKT_ERROR; + } + server->game = strndup(pkt, iLen); + + for (i = 0; i < (int)strlen(server->game) - 5; i++) + // clean the "/mods/" part from every entry + { + if (memcmp(&server->game[i], "\\mods\\", 6) == 0) + { + server->game[i] = ' '; + strcpy(&server->game[i + 1], &server->game[i + 6]); + } + } + add_rule(server, "game", server->game, NO_FLAGS); + + pkt += iLen; /* mods */ + + iDedicatedServer = pkt[0]; + if (iDedicatedServer) + { + add_rule(server, "dedicated", "Yes", NO_FLAGS); + } + else + { + add_rule(server, "dedicated", "No", NO_FLAGS); + } + + pkt += 1; /* unknown */ + + iSecsPlayed = swap_float_from_little(pkt); + + add_rule(server, "timeplayed", play_time(iSecsPlayed, 2), NO_FLAGS); + + pkt += 4; /* time played */ + + switch (pkt[0]) + { + case 3: + strcpy(str, "Joining"); + break; + case 4: + strcpy(str, "Playing"); + break; + case 5: + strcpy(str, "Debrief"); + break; + default: + strcpy(str, "Undefined"); + } + add_rule(server, "status", str, NO_FLAGS); + + pkt += 1; + + pkt += 3; /* unknown */ + + + switch (pkt[0]) + { + case 2: + strcpy(str, "COOP"); + break; + case 3: + strcpy(str, "SOLO"); + break; + case 4: + strcpy(str, "TEAM"); + break; + default: + sprintf(str, "UNKOWN %u", pkt[0]); + break; + } + + add_rule(server, "gamemode", str, NO_FLAGS); + + if (pkt[0] == 2) + { + add_rule(server, "mission", lpszMission, NO_FLAGS); + } + else + { + add_rule(server, "mission", "No Mission", NO_FLAGS); + } + + free(lpszMission); + + pkt += 1; /* Game Mode */ + + pkt += 3; /* unknown */ + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > LONG_GR_LEN)) + { + add_rule(server, "error", "MOTD too Long", NO_FLAGS); + return PKT_ERROR; + } + strncpy(str, pkt, sizeof(str)); + str[sizeof(str) - 1] = 0; + add_rule(server, "motd", str, NO_FLAGS); + pkt += iLen; /* MOTD */ + + iSpawnType = swap_long_from_little(pkt); + + switch (iSpawnType) + { + case 0: + strcpy(str, "None"); + break; + case 1: + strcpy(str, "Individual"); + break; + case 2: + strcpy(str, "Team"); + break; + case 3: + strcpy(str, "Infinite"); + break; + default: + strcpy(str, "Unknown"); + } + + add_rule(server, "spawntype", str, NO_FLAGS); + pkt += 4; /* spawn type */ + + iTemp = swap_float_from_little(pkt); + add_rule(server, "gametime", play_time(iTemp, 2), NO_FLAGS); + + iTemp = iTemp - iSecsPlayed; + + if (iTemp <= 0) + { + iTemp = 0; + } + add_rule(server, "remainingtime", play_time(iTemp, 2), NO_FLAGS); + pkt += 4; /* Game time */ + + + iTemp = swap_long_from_little(pkt); + if (iIgnoreServerPlayer) + { + iTemp--; + } + if (iTemp != server->num_players) + { + add_rule(server, "error", "Number of Players Mismatch", NO_FLAGS); + } + + + pkt += 4; /* player count 2 */ + + if (iIgnoreServerPlayer) + { + pkt += 5; // skip first player data + } + + for (i = 0; i < server->num_players; i++) + // for each player get binary data + { + player = server->players; + // first we must find the player - lets look for the tag + while (player && (player->team != i)) + { + player = player->next; + } + /* get to player - linked list is in reverse order */ + + if (player) + { + player->team = pkt[2]; + switch (player->team) + { + case 1: + player->team_name = "Red"; + break; + case 2: + player->team_name = "Blue"; + break; + case 3: + player->team_name = "Yellow"; + break; + case 4: + player->team_name = "Green"; + break; + case 5: + player->team_name = "Unassigned"; + break; + default: + player->team_name = "Not Known"; + break; + } + player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; + player->deaths = pkt[1]; + } + pkt += 5; /* player data*/ + } + + for (i = 0; i < 5; i++) + { + pkt += 8; /* team data who knows what they have in here */ + } + + pkt += 1; + iUseStartTimer = pkt[0]; // UseStartTimer + + pkt += 1; + + iTemp = flStartTimerSetPoint = swap_float_from_little(pkt); + // Start Timer Set Point + pkt += 4; + + if (iUseStartTimer) + { + add_rule(server, "usestarttime", "Yes", NO_FLAGS); + add_rule(server, "starttimeset", play_time(iTemp, 2), NO_FLAGS); + } + else + { + add_rule(server, "usestarttime", "No", NO_FLAGS); + add_rule(server, "starttimeset", play_time(0, 2), NO_FLAGS); + } + + if ((ServerVersion == VERSION_1_3) || // stuff added in patch 1.3 + (ServerVersion == VERSION_1_4)) + { + iTemp = swap_float_from_little(pkt); // Debrief Time + add_rule(server, "debrieftime", play_time(iTemp, 2), NO_FLAGS); + pkt += 4; + + iTemp = swap_float_from_little(pkt); // Respawn Min + add_rule(server, "respawnmin", play_time(iTemp, 2), NO_FLAGS); + pkt += 4; + + iTemp = swap_float_from_little(pkt); // Respawn Max + add_rule(server, "respawnmax", play_time(iTemp, 2), NO_FLAGS); + pkt += 4; + + iTemp = swap_float_from_little(pkt); // Respawn Invulnerable + add_rule(server, "respawnsafe", play_time(iTemp, 2), NO_FLAGS); + pkt += 4; + } + else + { + add_rule(server, "debrieftime", "Undefined", NO_FLAGS); + add_rule(server, "respawnmin", "Undefined", NO_FLAGS); + add_rule(server, "respawnmax", "Undefined", NO_FLAGS); + add_rule(server, "respawnsafe", "Undefined", NO_FLAGS); + + } + + + pkt += 4; // 4 + iTemp = pkt[0]; // Spawn Count + + if ((iSpawnType == 1) || (iSpawnType == 2)) + /* Individual or team */ + { + sprintf(str, "%u", iTemp); + } + else + /* else not used */ + { + sprintf(str, "%u", 0); + } + add_rule(server, "spawncount", str, NO_FLAGS); + pkt += 1; // 5 + + pkt += 4; // 9 + + iTemp = pkt[0]; // Allow Observers + if (iTemp) + { + strcpy(str, "Yes"); + } + else + /* else not used */ + { + strcpy(str, "No"); + } + add_rule(server, "allowobservers", str, NO_FLAGS); + pkt += 1; // 10 + + pkt += 3; // 13 + + // pkt += 13; + + if (iUseStartTimer) + { + iTemp = swap_float_from_little(pkt); // Start Timer Count + add_rule(server, "startwait", play_time(iTemp, 2), NO_FLAGS); + } + else + { + add_rule(server, "startwait", play_time(0, 2), NO_FLAGS); + } + pkt += 4; //17 + + iTemp = pkt[0]; // IFF + switch (iTemp) + { + case 0: + strcpy(str, "None"); + break; + case 1: + strcpy(str, "Reticule"); + break; + case 2: + strcpy(str, "Names"); + break; + default: + strcpy(str, "Unknown"); + break; + } + add_rule(server, "iff", str, NO_FLAGS); + pkt += 1; // 18 + + iTemp = pkt[0]; // Threat Indicator + if (iTemp) + { + add_rule(server, "ti", "ON ", NO_FLAGS); + } + else + { + add_rule(server, "ti", "OFF", NO_FLAGS); + } + + pkt += 1; // 19 + + pkt += 5; // 24 + + + iLen = swap_long_from_little(pkt); + pkt += 4; + if ((iLen < 1) || (iLen > SHORT_GR_LEN)) + { + add_rule(server, "error", "Restrictions too Long", NO_FLAGS); + return PKT_ERROR; + } + add_rule(server, "restrict", pkt, NO_FLAGS); + pkt += iLen; /* restrictions */ + + pkt += 23; + + /* + if ( ghostrecon_debug) print_packet( pkt, GrPayloadLen); + */ + + return DONE_FORCE; +} diff --git a/reference/ghostrecon/qstat_info.txt b/reference/ghostrecon/qstat_info.txt index 54a3566..b28b856 100644 --- a/reference/ghostrecon/qstat_info.txt +++ b/reference/ghostrecon/qstat_info.txt @@ -1,229 +1,229 @@ -/********* -QStat - Real-time game server stats -http://sourceforge.net/p/qstat/ -License: The Artistic License 2.0 -*********/ - -Ghost Recon - QStat notes -------------------------- - -The following Server Stats are pulled from the Ghost Recon Server - NOTE -many other stats continue to work as normal due to the base qstat program. - -$SERVERNAME - - The name of the GR Server. - -$PLAYERS - - The number of Players that are playing, oberving or in the Lobby - (note the ignoreserverplayer Argument above) - -$MAXPLAYERS - - The maximum players that the server will allow playing, oberving - or in the Lobby (note the ignoreserverplayer Argument above) - -$MAP - - The Name of the MAP that is being used (NOTE not the Mission) - -$GAME - - The Mods that the server is running. Ex: mp1; is the Desert - Seige Mod - -$(RULE:error) - - If an error occured there may be some detail here. IF the problm - occurred very early in the interpretation then $SERVERNAME will - hold the details. - -$(RULE:mission) - - The name of the Mission that the server is running. - -$(RULE:gamemode) - - What is the Game Mode that the server is in. Known values are - COOP, TEAM and SOLO - -$(RULE:missiontype) - - What is the Mission Type. Known Values are: Mission, Firefight, - Recon, Hamburger Hill, Last Man Standing, Sharpshooter, Search - And Rescue, Domination, and Seige. - -$(RULE:dedicated) - - Is this server Dedicated; Yes or No. - -$(RULE:status) - - What is the Playing Status of the Server, values are Playing, - Joining or Debrief. - -$(RULE:gametime) - - What is the Time limit for the Game. Values are 00:00, 05:00, - 10:00, 15:00 20:00, 25:00, 30:00, 45:00 and 60:00. The 00:00 - is for an unlimited game. The format of this uses the -ts, - -tc and -tsw command line options. - -$(RULE:timeplayed) - - How long has this game been playing. The format of this uses - the -ts, -tc and -tsw command line options. - -$(RULE:remainingtime) - - How much time is left in this game. The format of this uses - the -ts, -tc and -tsw command line options. - -$(RULE:version) - - What is the Version number reported by the server. Patch 1.2 = - 10.1010A, Patch 1.3 = 11.101A - -$(RULE:spawntype) - - What type of spawn is in use. Known Values are None, Infinite, - Individual and Team. - -$(RULE:spawncount) - - How many spawns are allowed. Enhancment possibility to add - $(IF:SPAWN) to filter out when spawntype is none. - -$(RULE:restrict) - - What Weapon restrictions are in force for the server. - -$(RULE:password) - - Does the Server have a join password defined Yes or No. - -$(RULE:ti) - - Is the server using the Threat Indicator. - -$(RULE:motd) - - What is the Message Of The Day - Note these can be quite big. - -$(RULE:patch) - - What is the patch level of the GR Server. - -$(RULE:usestarttime) - - Is the server configured to start a game after "starttimeset" - (Yes) OR does everyone need to click on ready (no). - -$(RULE:starttimeset) - - What time is configured to automatically start the next round. - -$(RULE:debrieftime) - - How long does the server wait at the Debrief screen after - a mission. - -$(RULE:respawnmin) - - How long must a dead player wait before he can repawn. - -$(RULE:respawnmax) - - What is the longest time that a user has to respawn. - -$(RULE:respawnsafe) - - How long after respawn is a player invulnerable/cannot damage - others. - -$(RULE:allowobservers) - - Does the server allow observers? Yes or No - -$(RULE:startwait) - - How long untill the automatic start timer forces the next game - to start. - -$(RULE:iff) - - What Identification - Friend or Foe is configured. None, - Reticule or Names - -$PLAYERNAME - - What is the Players Name. - -$TEAMNUM - - What Team Number is the Player On. Known Values are 1,2,3,4,5. - 1 is Team BLUE, 2 is Team Read, 3 is Team Yellow, 4 is - Team Green, 5 is Unassigned (observer or in lobby) - -$TEAMNAME - - What is the Name of the Team, see above. - -$DEATHS - - What is the health of this player. 0 Alive, 1 Dead. Note if the - player has spawns remaining this can change from 1 back to 0. - Enhancement possibility to add $HEALTH or $(RULE:health). - Hopefully RSE/UBI will add the Deaths, Frags, and Ping to the - availible information. If this happens then it would be better - to have a $HEALTH - -$(IF:DEATHS) and $(IFNOT:DEATHS) - - A Test to see if the player is dead. Usefull in this constuct: - $(IF:DEATHS)Dead$(ENDIF)$(IFNOT:DEATHS)Alive$(ENDIF) - -Ghost Recon communicates on two UDP ports and one TCP stream. Normally TCP -is on port 2346 and carries the game dialog. This is the port number -that is mentioned in the game so we use it and apply an offset to get the -port number for status queries. Port 2347 gives some high level server stats -and 2348 gives fairly low level server stats. QStat is designed around -a single port per server so the 2348 port is used. One down side to this -is the lack of many meaningful detail player stats (Deaths, frags, hit -percentage, ping etc.). I imagines that some of these are availible in -the TCP stream but that would be difficult to add to a program like QStat. - -The Ghost Recon packets are variable structures with a lot of string -lengths. This requires fairly defensive programming as Red Storm -Entertainment is not forthcoming with any details. - -This release adds support for the GhostRecon game. Number one note -is that Red Storm and UBI do not provide the information that many -Quake based users expect. Specifically they do not make Frags, Deaths -Connect Time or Pings availible - at least not as far as I can tell. -That said there are quite a few things that are availible and allow a -server administrator to make the status of his or her server available -to the public via the web. - -This change uses all undocumented interfaces to the Ghost Recon server -so will most likely break when you install a patch. It has been tested -against the Desert Seige update and several public servers. It should -work against the 1.2, 1.3, and 1.4 patches and Island Thunder add-on to -Ghost Recon. - -The Ghost Recon game type is GRS. For command-line queries, use -grs - -There is one query argument to this server, ignoreserverplayer. -This option controls whether the first player is ignored. Ghost Recon -requires that the dedicated server program take up one of the player slots -(always the first slot). The ignoreserverplayer option defaults to 'yes', -so the "server player" will normally not be seen. If you are running -a non-dedicated server, then set ignoreserverplayer to 'no' like this: - - -grs,ignoreserverplayer=no - -Otherwise you would not be able to display your own stats. - - +/********* +QStat - Real-time game server stats +http://sourceforge.net/p/qstat/ +License: The Artistic License 2.0 +*********/ + +Ghost Recon - QStat notes +------------------------- + +The following Server Stats are pulled from the Ghost Recon Server - NOTE +many other stats continue to work as normal due to the base qstat program. + +$SERVERNAME + + The name of the GR Server. + +$PLAYERS + + The number of Players that are playing, oberving or in the Lobby + (note the ignoreserverplayer Argument above) + +$MAXPLAYERS + + The maximum players that the server will allow playing, oberving + or in the Lobby (note the ignoreserverplayer Argument above) + +$MAP + + The Name of the MAP that is being used (NOTE not the Mission) + +$GAME + + The Mods that the server is running. Ex: mp1; is the Desert + Seige Mod + +$(RULE:error) + + If an error occured there may be some detail here. IF the problm + occurred very early in the interpretation then $SERVERNAME will + hold the details. + +$(RULE:mission) + + The name of the Mission that the server is running. + +$(RULE:gamemode) + + What is the Game Mode that the server is in. Known values are + COOP, TEAM and SOLO + +$(RULE:missiontype) + + What is the Mission Type. Known Values are: Mission, Firefight, + Recon, Hamburger Hill, Last Man Standing, Sharpshooter, Search + And Rescue, Domination, and Seige. + +$(RULE:dedicated) + + Is this server Dedicated; Yes or No. + +$(RULE:status) + + What is the Playing Status of the Server, values are Playing, + Joining or Debrief. + +$(RULE:gametime) + + What is the Time limit for the Game. Values are 00:00, 05:00, + 10:00, 15:00 20:00, 25:00, 30:00, 45:00 and 60:00. The 00:00 + is for an unlimited game. The format of this uses the -ts, + -tc and -tsw command line options. + +$(RULE:timeplayed) + + How long has this game been playing. The format of this uses + the -ts, -tc and -tsw command line options. + +$(RULE:remainingtime) + + How much time is left in this game. The format of this uses + the -ts, -tc and -tsw command line options. + +$(RULE:version) + + What is the Version number reported by the server. Patch 1.2 = + 10.1010A, Patch 1.3 = 11.101A + +$(RULE:spawntype) + + What type of spawn is in use. Known Values are None, Infinite, + Individual and Team. + +$(RULE:spawncount) + + How many spawns are allowed. Enhancment possibility to add + $(IF:SPAWN) to filter out when spawntype is none. + +$(RULE:restrict) + + What Weapon restrictions are in force for the server. + +$(RULE:password) + + Does the Server have a join password defined Yes or No. + +$(RULE:ti) + + Is the server using the Threat Indicator. + +$(RULE:motd) + + What is the Message Of The Day - Note these can be quite big. + +$(RULE:patch) + + What is the patch level of the GR Server. + +$(RULE:usestarttime) + + Is the server configured to start a game after "starttimeset" + (Yes) OR does everyone need to click on ready (no). + +$(RULE:starttimeset) + + What time is configured to automatically start the next round. + +$(RULE:debrieftime) + + How long does the server wait at the Debrief screen after + a mission. + +$(RULE:respawnmin) + + How long must a dead player wait before he can repawn. + +$(RULE:respawnmax) + + What is the longest time that a user has to respawn. + +$(RULE:respawnsafe) + + How long after respawn is a player invulnerable/cannot damage + others. + +$(RULE:allowobservers) + + Does the server allow observers? Yes or No + +$(RULE:startwait) + + How long untill the automatic start timer forces the next game + to start. + +$(RULE:iff) + + What Identification - Friend or Foe is configured. None, + Reticule or Names + +$PLAYERNAME + + What is the Players Name. + +$TEAMNUM + + What Team Number is the Player On. Known Values are 1,2,3,4,5. + 1 is Team BLUE, 2 is Team Read, 3 is Team Yellow, 4 is + Team Green, 5 is Unassigned (observer or in lobby) + +$TEAMNAME + + What is the Name of the Team, see above. + +$DEATHS + + What is the health of this player. 0 Alive, 1 Dead. Note if the + player has spawns remaining this can change from 1 back to 0. + Enhancement possibility to add $HEALTH or $(RULE:health). + Hopefully RSE/UBI will add the Deaths, Frags, and Ping to the + availible information. If this happens then it would be better + to have a $HEALTH + +$(IF:DEATHS) and $(IFNOT:DEATHS) + + A Test to see if the player is dead. Usefull in this constuct: + $(IF:DEATHS)Dead$(ENDIF)$(IFNOT:DEATHS)Alive$(ENDIF) + +Ghost Recon communicates on two UDP ports and one TCP stream. Normally TCP +is on port 2346 and carries the game dialog. This is the port number +that is mentioned in the game so we use it and apply an offset to get the +port number for status queries. Port 2347 gives some high level server stats +and 2348 gives fairly low level server stats. QStat is designed around +a single port per server so the 2348 port is used. One down side to this +is the lack of many meaningful detail player stats (Deaths, frags, hit +percentage, ping etc.). I imagines that some of these are availible in +the TCP stream but that would be difficult to add to a program like QStat. + +The Ghost Recon packets are variable structures with a lot of string +lengths. This requires fairly defensive programming as Red Storm +Entertainment is not forthcoming with any details. + +This release adds support for the GhostRecon game. Number one note +is that Red Storm and UBI do not provide the information that many +Quake based users expect. Specifically they do not make Frags, Deaths +Connect Time or Pings availible - at least not as far as I can tell. +That said there are quite a few things that are availible and allow a +server administrator to make the status of his or her server available +to the public via the web. + +This change uses all undocumented interfaces to the Ghost Recon server +so will most likely break when you install a patch. It has been tested +against the Desert Seige update and several public servers. It should +work against the 1.2, 1.3, and 1.4 patches and Island Thunder add-on to +Ghost Recon. + +The Ghost Recon game type is GRS. For command-line queries, use -grs + +There is one query argument to this server, ignoreserverplayer. +This option controls whether the first player is ignored. Ghost Recon +requires that the dedicated server program take up one of the player slots +(always the first slot). The ignoreserverplayer option defaults to 'yes', +so the "server player" will normally not be seen. If you are running +a non-dedicated server, then set ignoreserverplayer to 'no' like this: + + -grs,ignoreserverplayer=no + +Otherwise you would not be able to display your own stats. + + Ghost Recon support provided by Bob Marriott. \ No newline at end of file diff --git a/reference/gtr2/lgsl.txt b/reference/gtr2/lgsl.txt index 62051fd..53a541d 100644 --- a/reference/gtr2/lgsl.txt +++ b/reference/gtr2/lgsl.txt @@ -1,40 +1,40 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_15(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ - - fwrite($lgsl_fp, "GTR2_Direct_IP_Search\x00"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $buffer = str_replace("\xFE", "\xFF", $buffer); - $buffer = explode("\xFF", $buffer); - - $server['s']['name'] = $buffer[3]; - $server['s']['game'] = $buffer[7]; - $server['e']['version'] = $buffer[11]; - $server['e']['hostport'] = $buffer[15]; - $server['s']['map'] = $buffer[19]; - $server['s']['players'] = $buffer[25]; - $server['s']['playersmax'] = $buffer[27]; - $server['e']['gamemode'] = $buffer[31]; - - // DOES NOT RETURN PLAYER INFORMATION - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + function lgsl_query_15(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ + + fwrite($lgsl_fp, "GTR2_Direct_IP_Search\x00"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $buffer = str_replace("\xFE", "\xFF", $buffer); + $buffer = explode("\xFF", $buffer); + + $server['s']['name'] = $buffer[3]; + $server['s']['game'] = $buffer[7]; + $server['e']['version'] = $buffer[11]; + $server['e']['hostport'] = $buffer[15]; + $server['s']['map'] = $buffer[19]; + $server['s']['players'] = $buffer[25]; + $server['s']['playersmax'] = $buffer[27]; + $server['e']['gamemode'] = $buffer[31]; + + // DOES NOT RETURN PLAYER INFORMATION + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/haze/qstat.txt b/reference/haze/qstat.txt index e037b5f..260731b 100644 --- a/reference/haze/qstat.txt +++ b/reference/haze/qstat.txt @@ -1,716 +1,716 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - #define HAZE_BASIC_INFO 0x01 -#define HAZE_GAME_RULES 0x02 -#define HAZE_PLAYER_INFO 0x04 -#define HAZE_TEAM_INFO 0x08 - - -// Format: -// 1 - 8: Query Request -// 9 - 12: Query Header -// 13: Query ID - -// Query ID is made up of the following -// 0x01: Basic Info -// 0x02: Game Rules -// 0x03: Player Information -// 0x04: Team Information -unsigned char haze_status_query[] = { - 'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y', - 0x10,0x20,0x30,0x40, - 0x0A -}; - -// Format: -// 1 - 8: Query Request -// 9 - 12: Query Header -// 13: Query ID - -// Query ID is made up of the following -// 0x01: Basic Info -// 0x02: Game Rules -// 0x03: Player Information -// 0x04: Team Information -unsigned char haze_player_query[] = { - 'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y', - 0x10,0x20,0x30,0x40, - 0x03 -}; - - - { - /* HAZE PROTOCOL */ - HAZE_SERVER, /* id */ - "HAZES", /* type_prefix */ - "hazes", /* type_string */ - "-hazes", /* type_option */ - "Haze Protocol", /* game_name */ - 0, /* master */ - 0, /* default_port */ - 0, /* port_offset */ - TF_SINGLE_QUERY, /* flags */ - "gametype", /* game_rule */ - "HAZE", /* template_var */ - (char*) &haze_status_query, /* status_packet */ - sizeof( haze_status_query), /* status_len */ - (char*) &haze_player_query, /* player_packet */ - sizeof( haze_player_query), /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_gs2_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_gs2_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_haze_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_haze_packet, /* packet_func */ -}, - - -/* - * qstat 2.8 - * by Steve Jankowski - * - * New Haze query protocol - * Copyright 2005 Steven Hartland - * - * Licensed under the Artistic License, see LICENSE.txt for license terms - * - */ - -#include -#ifndef _WIN32 -#include -#include -#endif -#include -#include - -#include "debug.h" -#include "qstat.h" -#include "packet_manip.h" - - -// Format: -// 1 - 8: Challenge Request / Response -char haze_challenge[] = { - 'f', 'r', 'd', 'c', '_', '_', '_', '_' -}; - -int process_haze_packet( struct qserver *server ); - -// Player headers -#define PLAYER_NAME_HEADER 1 -#define PLAYER_SCORE_HEADER 2 -#define PLAYER_DEATHS_HEADER 3 -#define PLAYER_PING_HEADER 4 -#define PLAYER_KILLS_HEADER 5 -#define PLAYER_TEAM_HEADER 6 -#define PLAYER_OTHER_HEADER 7 - -// Team headers -#define TEAM_NAME_HEADER 1 -#define TEAM_OTHER_HEADER 2 - -// Challenge response algorithum -// Before sending a qr2 query (type 0x00) the client must first send a -// challenge request (type 0x09). The host will respond with the same -// packet type containing a string signed integer. -// -// Once the challenge is received the client should convert the string to a -// network byte order integer and embed it in the keys query. -// -// Example: -// -// challenge request: [0xFE][0xFD][0x09][0x.. 4-byte-instance] -// challenge response: [0x09][0x.. 4-byte-instance]["-1287574694"] -// query: [0xFE][0xFD][0x00][0x.. 4-byte-instance][0xb3412b5a "-1287574694"] -// - -query_status_t deal_with_haze_packet( struct qserver *server, char *rawpkt, int pktlen ) -{ - char *ptr = rawpkt; - unsigned int pkt_id; - unsigned short len; - unsigned char pkt_max, pkt_index; - - debug( 2, "packet..." ); - - if ( pktlen < 8 ) - { - // invalid packet - malformed_packet( server, "too short" ); - return PKT_ERROR; - } - - if ( 0 == strncmp( ptr, "frdcr", 5 ) ) - { - // challenge response - ptr += 8; - server->challenge = 1; - - // Correct the stats due to two phase protocol - server->retry1++; - server->n_packets--; - if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST ) - { - //server->n_requests--; - } - else - { - server->n_retries--; - } - return send_haze_request_packet( server ); - } - - if ( pktlen < 12 ) - { - // invalid packet - malformed_packet( server, "too short" ); - return PKT_ERROR; - } - - server->n_servers++; - if ( server->server_name == NULL ) - { - server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 ); - } - else - { - gettimeofday( &server->packet_time1, NULL); - } - - // Query version ID - ptr += 4; - - // Could check the header here should - // match the 4 byte id sent - memcpy( &pkt_id, ptr, 4 ); - ptr += 4; - - - // Max plackets - pkt_max = ((unsigned char)*ptr); - ptr++; - - // Packet ID - pkt_index = ((unsigned char)*ptr); - ptr++; - - // Query Length - //len = (unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8); - //len = swap_short_from_little( ptr ); - debug( 1, "%04hx, %04hx", (unsigned short)ptr[0], ((unsigned short)ptr[1] << 8) ); - //len = (unsigned short)(unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8); - // TODO: fix this crap - memcpy( &len, ptr+1, 1 ); - //memcpy( &len+1, ptr, 1 ); - //memcpy( &len, ptr, 2 ); - ptr += 2; - - debug( 1, "pkt_index = %d, pkt_max = %d, len = %d", pkt_index, pkt_max, len ); - if ( 0 != pkt_max ) - { - // not a single packet response or callback - debug( 2, "pkt_max %d", pkt_max ); - - if ( 0 == pkt_index ) - { - // to prevent reprocessing when we get the call back - // override the packet flag so it looks like a single - // packet response - rawpkt[8] = '\0'; - } - - // add the packet recalcing maxes - if ( ! add_packet( server, pkt_id, pkt_index, pkt_max, pktlen, rawpkt, 1 ) ) - { - // fatal error e.g. out of memory - return MEM_ERROR; - } - - // combine_packets will call us recursively - return combine_packets( server ); - } - - // if we get here we have what should be a full packet - return process_haze_packet( server ); -} - -query_status_t deal_with_haze_status( struct qserver *server, char *rawpkt, int pktlen ) -{ - char *pkt = rawpkt; - int len; - debug( 1, "status packet" ); - - - // Server name - server->server_name = strdup( pkt ); - pkt += strlen( pkt ) + 1; - - // gametype - add_rule( server, "gametype", pkt, NO_FLAGS ); - pkt += strlen( pkt ) + 1; - - // map - len = strlen( pkt ); - // remove .res from map names - if ( 0 == strncmp( pkt + len - 4, ".res", 4 ) ) - { - *(pkt + len - 4) = '\0'; - } - server->map_name = strdup( pkt ); - pkt += len + 1; - - // num players - server->num_players = atoi( pkt ); - pkt += strlen( pkt ) + 1; - - // max_players - server->max_players = atoi( pkt ); - pkt += strlen( pkt ) + 1; - - // hostport - change_server_port( server, atoi( pkt ), 0 ); - pkt += strlen( pkt ) + 1; - - return DONE_FORCE; -} - -int process_haze_packet( struct qserver *server ) -{ - unsigned char state = 0; - unsigned char no_players = 0; - unsigned char total_players = 0; - unsigned char no_teams = 0; - unsigned char total_teams = 0; - int pkt_index = 0; - SavedData *fragment; - - debug( 2, "processing packet..." ); - - while ( NULL != ( fragment = get_packet_fragment( pkt_index++ ) ) ) - { - int pktlen = fragment->datalen; - char *ptr = fragment->data; - char *end = ptr + pktlen; - debug( 2, "processing fragment[%d]...", fragment->pkt_index ); - - // check we have a full header - if ( pktlen < 12 ) - { - // invalid packet - malformed_packet( server, "too short" ); - return PKT_ERROR; - } - - // skip over the header - //server->protocol_version = atoi( val+1 ); - ptr += 12; - - // 4 * null's signifies the end of a section - - // Basic Info - while ( 0 == state && ptr < end ) - { - // name value pairs null seperated - char *var, *val; - int var_len, val_len; - - if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] ) - { - // end of rules - state++; - ptr += 4; - break; - } - - var = ptr; - var_len = strlen( var ); - ptr += var_len + 1; - - if ( ptr + 1 > end ) - { - malformed_packet( server, "no basic value" ); - return PKT_ERROR; - } - - val = ptr; - val_len = strlen( val ); - ptr += val_len + 1; - debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len ); - - // Lets see what we've got - if ( 0 == strcmp( var, "serverName" ) ) - { - server->server_name = strdup( val ); - } - else if( 0 == strcmp( var, "map" ) ) - { - // remove .res from map names - if ( 0 == strncmp( val + val_len - 4, ".res", 4 ) ) - { - *(val + val_len - 4) = '\0'; - } - server->map_name = strdup( val ); - } - else if( 0 == strcmp( var, "maxPlayers" ) ) - { - server->max_players = atoi( val ); - - } - else if( 0 == strcmp( var, "currentPlayers" ) ) - { - server->num_players = no_players = atoi( val ); - } - else - { - add_rule( server, var, val, NO_FLAGS ); - } - } - - // rules - while ( 1 == state && ptr < end ) - { - // name value pairs null seperated - char *var, *val; - int var_len, val_len; - - if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] ) - { - // end of basic - state++; - ptr += 4; - break; - } - var = ptr; - var_len = strlen( var ); - ptr += var_len + 1; - - if ( ptr + 1 > end ) - { - malformed_packet( server, "no basic value" ); - return PKT_ERROR; - } - - val = ptr; - val_len = strlen( val ); - ptr += val_len + 1; - debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len ); - - // add the rule - add_rule( server, var, val, NO_FLAGS ); - } - - // players - while ( 2 == state && ptr < end ) - { - // first we have the header - char *header = ptr; - int head_len = strlen( header ); - ptr += head_len + 1; - - if ( ptr+2 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] ) - { - // end of player headers - state++; - ptr += 2; - break; - } - - if ( 0 == head_len ) - { - // no more info - debug( 3, "All done" ); - return DONE_FORCE; - } - - debug( 2, "player header '%s'", header ); - - if ( ptr > end ) - { - malformed_packet( server, "no details for header '%s'", header ); - return PKT_ERROR; - } - - } - - while ( 3 == state && ptr < end ) - { - char *header = ptr; - int head_len = strlen( header ); - int header_type; - // the next byte is the starting number - total_players = *ptr++; - - if ( 0 == strcmp( header, "player_" ) || 0 == strcmp( header, "name_" ) ) - { - header_type = PLAYER_NAME_HEADER; - } - else if ( 0 == strcmp( header, "score_" ) ) - { - header_type = PLAYER_SCORE_HEADER; - } - else if ( 0 == strcmp( header, "deaths_" ) ) - { - header_type = PLAYER_DEATHS_HEADER; - } - else if ( 0 == strcmp( header, "ping_" ) ) - { - header_type = PLAYER_PING_HEADER; - } - else if ( 0 == strcmp( header, "kills_" ) ) - { - header_type = PLAYER_KILLS_HEADER; - } - else if ( 0 == strcmp( header, "team_" ) ) - { - header_type = PLAYER_TEAM_HEADER; - } - else - { - header_type = PLAYER_OTHER_HEADER; - } - - while( ptr < end ) - { - // now each player details - // add the player - struct player *player; - char *val; - int val_len; - - // check for end of this headers player info - if ( 0x00 == *ptr ) - { - debug( 3, "end of '%s' detail", header ); - ptr++; - // Note: can't check ( total_players != no_players ) here as we may have more packets - if ( ptr < end && 0x00 == *ptr ) - { - debug( 3, "end of players" ); - // end of all player headers / detail - state = 2; - ptr++; - } - break; - } - - player = get_player_by_number( server, total_players ); - if ( NULL == player ) - { - player = add_player( server, total_players ); - } - - if ( ptr >= end ) - { - malformed_packet( server, "short player detail" ); - return PKT_ERROR; - } - val = ptr; - val_len = strlen( val ); - ptr += val_len + 1; - - debug( 2, "Player[%d][%s]=%s\n", total_players, header, val ); - - // lets see what we got - switch( header_type ) - { - case PLAYER_NAME_HEADER: - player->name = strdup( val ); - break; - - case PLAYER_SCORE_HEADER: - player->score = atoi( val ); - break; - - case PLAYER_DEATHS_HEADER: - player->deaths = atoi( val ); - break; - - case PLAYER_PING_HEADER: - player->ping = atoi( val ); - break; - - case PLAYER_KILLS_HEADER: - player->frags = atoi( val ); - break; - - case PLAYER_TEAM_HEADER: - player->team = atoi( val ); - break; - - case PLAYER_OTHER_HEADER: - default: - if ( '_' == header[head_len-1] ) - { - header[head_len-1] = '\0'; - player_add_info( player, header, val, NO_FLAGS ); - header[head_len-1] = '_'; - } - else - { - player_add_info( player, header, val, NO_FLAGS ); - } - break; - } - - total_players++; - - if ( total_players > no_players ) - { - malformed_packet( server, "to many players %d > %d", total_players, no_players ); - return PKT_ERROR; - } - } - } - - if ( 3 == state ) - { - no_teams = (unsigned char)*ptr; - ptr++; - - debug( 2, "No teams:%d\n", no_teams ); - state = 3; - } - - while ( 4 == state && ptr < end ) - { - // first we have the header - char *header = ptr; - int head_len = strlen( header ); - int header_type; - ptr += head_len + 1; - - if ( 0 == head_len ) - { - // no more info - debug( 3, "All done" ); - return DONE_FORCE; - } - - debug( 2, "team header '%s'", header ); - if ( 0 == strcmp( header, "team_t" ) ) - { - header_type = TEAM_NAME_HEADER; - } - else - { - header_type = TEAM_OTHER_HEADER; - } - - // the next byte is the starting number - total_teams = *ptr++; - - while( ptr < end ) - { - // now each teams details - char *val; - int val_len; - char rule[512]; - - if ( ptr >= end ) - { - malformed_packet( server, "short team detail" ); - return PKT_ERROR; - } - val = ptr; - val_len = strlen( val ); - ptr += val_len + 1; - - debug( 2, "Team[%d][%s]=%s\n", total_teams, header, val ); - - // lets see what we got - switch ( header_type ) - { - case TEAM_NAME_HEADER: - // BF being stupid again teams 1 based instead of 0 - players_set_teamname( server, total_teams + 1, val ); - // N.B. yes no break - - case TEAM_OTHER_HEADER: - default: - // add as a server rule - sprintf( rule, "%s%d", header, total_teams ); - add_rule( server, rule, val, NO_FLAGS ); - break; - } - - total_teams++; - if ( 0x00 == *ptr ) - { - // end of this headers teams - ptr++; - break; - } - } - } - } - - return DONE_FORCE; -} - -query_status_t send_haze_request_packet( struct qserver *server ) -{ - char *packet; - char query_buf[128]; - size_t len; - unsigned char required = HAZE_BASIC_INFO; - - if ( get_server_rules ) - { - required |= HAZE_GAME_RULES; - server->flags |= TF_PLAYER_QUERY; - } - - if ( get_player_info ) - { - required |= HAZE_PLAYER_INFO; - required |= HAZE_TEAM_INFO; - server->flags |= TF_RULES_QUERY; - } - - server->flags |= TF_STATUS_QUERY; - - if ( server->challenge ) - { - // we've recieved a challenge response, send the query + challenge id - len = sprintf( - query_buf, - "frdquery%c%c%c%c%c", - (unsigned char)(server->challenge >> 24), - (unsigned char)(server->challenge >> 16), - (unsigned char)(server->challenge >> 8), - (unsigned char)(server->challenge >> 0), - required - ); - packet = query_buf; - } - else - { - // Either basic v3 protocol or challenge request - packet = haze_challenge; - len = sizeof( haze_challenge ); - } - - return send_packet( server, packet, len ); -} - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + #define HAZE_BASIC_INFO 0x01 +#define HAZE_GAME_RULES 0x02 +#define HAZE_PLAYER_INFO 0x04 +#define HAZE_TEAM_INFO 0x08 + + +// Format: +// 1 - 8: Query Request +// 9 - 12: Query Header +// 13: Query ID + +// Query ID is made up of the following +// 0x01: Basic Info +// 0x02: Game Rules +// 0x03: Player Information +// 0x04: Team Information +unsigned char haze_status_query[] = { + 'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y', + 0x10,0x20,0x30,0x40, + 0x0A +}; + +// Format: +// 1 - 8: Query Request +// 9 - 12: Query Header +// 13: Query ID + +// Query ID is made up of the following +// 0x01: Basic Info +// 0x02: Game Rules +// 0x03: Player Information +// 0x04: Team Information +unsigned char haze_player_query[] = { + 'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y', + 0x10,0x20,0x30,0x40, + 0x03 +}; + + + { + /* HAZE PROTOCOL */ + HAZE_SERVER, /* id */ + "HAZES", /* type_prefix */ + "hazes", /* type_string */ + "-hazes", /* type_option */ + "Haze Protocol", /* game_name */ + 0, /* master */ + 0, /* default_port */ + 0, /* port_offset */ + TF_SINGLE_QUERY, /* flags */ + "gametype", /* game_rule */ + "HAZE", /* template_var */ + (char*) &haze_status_query, /* status_packet */ + sizeof( haze_status_query), /* status_len */ + (char*) &haze_player_query, /* player_packet */ + sizeof( haze_player_query), /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_gs2_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_gs2_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_haze_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_haze_packet, /* packet_func */ +}, + + +/* + * qstat 2.8 + * by Steve Jankowski + * + * New Haze query protocol + * Copyright 2005 Steven Hartland + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + * + */ + +#include +#ifndef _WIN32 +#include +#include +#endif +#include +#include + +#include "debug.h" +#include "qstat.h" +#include "packet_manip.h" + + +// Format: +// 1 - 8: Challenge Request / Response +char haze_challenge[] = { + 'f', 'r', 'd', 'c', '_', '_', '_', '_' +}; + +int process_haze_packet( struct qserver *server ); + +// Player headers +#define PLAYER_NAME_HEADER 1 +#define PLAYER_SCORE_HEADER 2 +#define PLAYER_DEATHS_HEADER 3 +#define PLAYER_PING_HEADER 4 +#define PLAYER_KILLS_HEADER 5 +#define PLAYER_TEAM_HEADER 6 +#define PLAYER_OTHER_HEADER 7 + +// Team headers +#define TEAM_NAME_HEADER 1 +#define TEAM_OTHER_HEADER 2 + +// Challenge response algorithum +// Before sending a qr2 query (type 0x00) the client must first send a +// challenge request (type 0x09). The host will respond with the same +// packet type containing a string signed integer. +// +// Once the challenge is received the client should convert the string to a +// network byte order integer and embed it in the keys query. +// +// Example: +// +// challenge request: [0xFE][0xFD][0x09][0x.. 4-byte-instance] +// challenge response: [0x09][0x.. 4-byte-instance]["-1287574694"] +// query: [0xFE][0xFD][0x00][0x.. 4-byte-instance][0xb3412b5a "-1287574694"] +// + +query_status_t deal_with_haze_packet( struct qserver *server, char *rawpkt, int pktlen ) +{ + char *ptr = rawpkt; + unsigned int pkt_id; + unsigned short len; + unsigned char pkt_max, pkt_index; + + debug( 2, "packet..." ); + + if ( pktlen < 8 ) + { + // invalid packet + malformed_packet( server, "too short" ); + return PKT_ERROR; + } + + if ( 0 == strncmp( ptr, "frdcr", 5 ) ) + { + // challenge response + ptr += 8; + server->challenge = 1; + + // Correct the stats due to two phase protocol + server->retry1++; + server->n_packets--; + if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST ) + { + //server->n_requests--; + } + else + { + server->n_retries--; + } + return send_haze_request_packet( server ); + } + + if ( pktlen < 12 ) + { + // invalid packet + malformed_packet( server, "too short" ); + return PKT_ERROR; + } + + server->n_servers++; + if ( server->server_name == NULL ) + { + server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 ); + } + else + { + gettimeofday( &server->packet_time1, NULL); + } + + // Query version ID + ptr += 4; + + // Could check the header here should + // match the 4 byte id sent + memcpy( &pkt_id, ptr, 4 ); + ptr += 4; + + + // Max plackets + pkt_max = ((unsigned char)*ptr); + ptr++; + + // Packet ID + pkt_index = ((unsigned char)*ptr); + ptr++; + + // Query Length + //len = (unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8); + //len = swap_short_from_little( ptr ); + debug( 1, "%04hx, %04hx", (unsigned short)ptr[0], ((unsigned short)ptr[1] << 8) ); + //len = (unsigned short)(unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8); + // TODO: fix this crap + memcpy( &len, ptr+1, 1 ); + //memcpy( &len+1, ptr, 1 ); + //memcpy( &len, ptr, 2 ); + ptr += 2; + + debug( 1, "pkt_index = %d, pkt_max = %d, len = %d", pkt_index, pkt_max, len ); + if ( 0 != pkt_max ) + { + // not a single packet response or callback + debug( 2, "pkt_max %d", pkt_max ); + + if ( 0 == pkt_index ) + { + // to prevent reprocessing when we get the call back + // override the packet flag so it looks like a single + // packet response + rawpkt[8] = '\0'; + } + + // add the packet recalcing maxes + if ( ! add_packet( server, pkt_id, pkt_index, pkt_max, pktlen, rawpkt, 1 ) ) + { + // fatal error e.g. out of memory + return MEM_ERROR; + } + + // combine_packets will call us recursively + return combine_packets( server ); + } + + // if we get here we have what should be a full packet + return process_haze_packet( server ); +} + +query_status_t deal_with_haze_status( struct qserver *server, char *rawpkt, int pktlen ) +{ + char *pkt = rawpkt; + int len; + debug( 1, "status packet" ); + + + // Server name + server->server_name = strdup( pkt ); + pkt += strlen( pkt ) + 1; + + // gametype + add_rule( server, "gametype", pkt, NO_FLAGS ); + pkt += strlen( pkt ) + 1; + + // map + len = strlen( pkt ); + // remove .res from map names + if ( 0 == strncmp( pkt + len - 4, ".res", 4 ) ) + { + *(pkt + len - 4) = '\0'; + } + server->map_name = strdup( pkt ); + pkt += len + 1; + + // num players + server->num_players = atoi( pkt ); + pkt += strlen( pkt ) + 1; + + // max_players + server->max_players = atoi( pkt ); + pkt += strlen( pkt ) + 1; + + // hostport + change_server_port( server, atoi( pkt ), 0 ); + pkt += strlen( pkt ) + 1; + + return DONE_FORCE; +} + +int process_haze_packet( struct qserver *server ) +{ + unsigned char state = 0; + unsigned char no_players = 0; + unsigned char total_players = 0; + unsigned char no_teams = 0; + unsigned char total_teams = 0; + int pkt_index = 0; + SavedData *fragment; + + debug( 2, "processing packet..." ); + + while ( NULL != ( fragment = get_packet_fragment( pkt_index++ ) ) ) + { + int pktlen = fragment->datalen; + char *ptr = fragment->data; + char *end = ptr + pktlen; + debug( 2, "processing fragment[%d]...", fragment->pkt_index ); + + // check we have a full header + if ( pktlen < 12 ) + { + // invalid packet + malformed_packet( server, "too short" ); + return PKT_ERROR; + } + + // skip over the header + //server->protocol_version = atoi( val+1 ); + ptr += 12; + + // 4 * null's signifies the end of a section + + // Basic Info + while ( 0 == state && ptr < end ) + { + // name value pairs null seperated + char *var, *val; + int var_len, val_len; + + if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] ) + { + // end of rules + state++; + ptr += 4; + break; + } + + var = ptr; + var_len = strlen( var ); + ptr += var_len + 1; + + if ( ptr + 1 > end ) + { + malformed_packet( server, "no basic value" ); + return PKT_ERROR; + } + + val = ptr; + val_len = strlen( val ); + ptr += val_len + 1; + debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len ); + + // Lets see what we've got + if ( 0 == strcmp( var, "serverName" ) ) + { + server->server_name = strdup( val ); + } + else if( 0 == strcmp( var, "map" ) ) + { + // remove .res from map names + if ( 0 == strncmp( val + val_len - 4, ".res", 4 ) ) + { + *(val + val_len - 4) = '\0'; + } + server->map_name = strdup( val ); + } + else if( 0 == strcmp( var, "maxPlayers" ) ) + { + server->max_players = atoi( val ); + + } + else if( 0 == strcmp( var, "currentPlayers" ) ) + { + server->num_players = no_players = atoi( val ); + } + else + { + add_rule( server, var, val, NO_FLAGS ); + } + } + + // rules + while ( 1 == state && ptr < end ) + { + // name value pairs null seperated + char *var, *val; + int var_len, val_len; + + if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] ) + { + // end of basic + state++; + ptr += 4; + break; + } + var = ptr; + var_len = strlen( var ); + ptr += var_len + 1; + + if ( ptr + 1 > end ) + { + malformed_packet( server, "no basic value" ); + return PKT_ERROR; + } + + val = ptr; + val_len = strlen( val ); + ptr += val_len + 1; + debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len ); + + // add the rule + add_rule( server, var, val, NO_FLAGS ); + } + + // players + while ( 2 == state && ptr < end ) + { + // first we have the header + char *header = ptr; + int head_len = strlen( header ); + ptr += head_len + 1; + + if ( ptr+2 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] ) + { + // end of player headers + state++; + ptr += 2; + break; + } + + if ( 0 == head_len ) + { + // no more info + debug( 3, "All done" ); + return DONE_FORCE; + } + + debug( 2, "player header '%s'", header ); + + if ( ptr > end ) + { + malformed_packet( server, "no details for header '%s'", header ); + return PKT_ERROR; + } + + } + + while ( 3 == state && ptr < end ) + { + char *header = ptr; + int head_len = strlen( header ); + int header_type; + // the next byte is the starting number + total_players = *ptr++; + + if ( 0 == strcmp( header, "player_" ) || 0 == strcmp( header, "name_" ) ) + { + header_type = PLAYER_NAME_HEADER; + } + else if ( 0 == strcmp( header, "score_" ) ) + { + header_type = PLAYER_SCORE_HEADER; + } + else if ( 0 == strcmp( header, "deaths_" ) ) + { + header_type = PLAYER_DEATHS_HEADER; + } + else if ( 0 == strcmp( header, "ping_" ) ) + { + header_type = PLAYER_PING_HEADER; + } + else if ( 0 == strcmp( header, "kills_" ) ) + { + header_type = PLAYER_KILLS_HEADER; + } + else if ( 0 == strcmp( header, "team_" ) ) + { + header_type = PLAYER_TEAM_HEADER; + } + else + { + header_type = PLAYER_OTHER_HEADER; + } + + while( ptr < end ) + { + // now each player details + // add the player + struct player *player; + char *val; + int val_len; + + // check for end of this headers player info + if ( 0x00 == *ptr ) + { + debug( 3, "end of '%s' detail", header ); + ptr++; + // Note: can't check ( total_players != no_players ) here as we may have more packets + if ( ptr < end && 0x00 == *ptr ) + { + debug( 3, "end of players" ); + // end of all player headers / detail + state = 2; + ptr++; + } + break; + } + + player = get_player_by_number( server, total_players ); + if ( NULL == player ) + { + player = add_player( server, total_players ); + } + + if ( ptr >= end ) + { + malformed_packet( server, "short player detail" ); + return PKT_ERROR; + } + val = ptr; + val_len = strlen( val ); + ptr += val_len + 1; + + debug( 2, "Player[%d][%s]=%s\n", total_players, header, val ); + + // lets see what we got + switch( header_type ) + { + case PLAYER_NAME_HEADER: + player->name = strdup( val ); + break; + + case PLAYER_SCORE_HEADER: + player->score = atoi( val ); + break; + + case PLAYER_DEATHS_HEADER: + player->deaths = atoi( val ); + break; + + case PLAYER_PING_HEADER: + player->ping = atoi( val ); + break; + + case PLAYER_KILLS_HEADER: + player->frags = atoi( val ); + break; + + case PLAYER_TEAM_HEADER: + player->team = atoi( val ); + break; + + case PLAYER_OTHER_HEADER: + default: + if ( '_' == header[head_len-1] ) + { + header[head_len-1] = '\0'; + player_add_info( player, header, val, NO_FLAGS ); + header[head_len-1] = '_'; + } + else + { + player_add_info( player, header, val, NO_FLAGS ); + } + break; + } + + total_players++; + + if ( total_players > no_players ) + { + malformed_packet( server, "to many players %d > %d", total_players, no_players ); + return PKT_ERROR; + } + } + } + + if ( 3 == state ) + { + no_teams = (unsigned char)*ptr; + ptr++; + + debug( 2, "No teams:%d\n", no_teams ); + state = 3; + } + + while ( 4 == state && ptr < end ) + { + // first we have the header + char *header = ptr; + int head_len = strlen( header ); + int header_type; + ptr += head_len + 1; + + if ( 0 == head_len ) + { + // no more info + debug( 3, "All done" ); + return DONE_FORCE; + } + + debug( 2, "team header '%s'", header ); + if ( 0 == strcmp( header, "team_t" ) ) + { + header_type = TEAM_NAME_HEADER; + } + else + { + header_type = TEAM_OTHER_HEADER; + } + + // the next byte is the starting number + total_teams = *ptr++; + + while( ptr < end ) + { + // now each teams details + char *val; + int val_len; + char rule[512]; + + if ( ptr >= end ) + { + malformed_packet( server, "short team detail" ); + return PKT_ERROR; + } + val = ptr; + val_len = strlen( val ); + ptr += val_len + 1; + + debug( 2, "Team[%d][%s]=%s\n", total_teams, header, val ); + + // lets see what we got + switch ( header_type ) + { + case TEAM_NAME_HEADER: + // BF being stupid again teams 1 based instead of 0 + players_set_teamname( server, total_teams + 1, val ); + // N.B. yes no break + + case TEAM_OTHER_HEADER: + default: + // add as a server rule + sprintf( rule, "%s%d", header, total_teams ); + add_rule( server, rule, val, NO_FLAGS ); + break; + } + + total_teams++; + if ( 0x00 == *ptr ) + { + // end of this headers teams + ptr++; + break; + } + } + } + } + + return DONE_FORCE; +} + +query_status_t send_haze_request_packet( struct qserver *server ) +{ + char *packet; + char query_buf[128]; + size_t len; + unsigned char required = HAZE_BASIC_INFO; + + if ( get_server_rules ) + { + required |= HAZE_GAME_RULES; + server->flags |= TF_PLAYER_QUERY; + } + + if ( get_player_info ) + { + required |= HAZE_PLAYER_INFO; + required |= HAZE_TEAM_INFO; + server->flags |= TF_RULES_QUERY; + } + + server->flags |= TF_STATUS_QUERY; + + if ( server->challenge ) + { + // we've recieved a challenge response, send the query + challenge id + len = sprintf( + query_buf, + "frdquery%c%c%c%c%c", + (unsigned char)(server->challenge >> 24), + (unsigned char)(server->challenge >> 16), + (unsigned char)(server->challenge >> 8), + (unsigned char)(server->challenge >> 0), + required + ); + packet = query_buf; + } + else + { + // Either basic v3 protocol or challenge request + packet = haze_challenge; + len = sizeof( haze_challenge ); + } + + return send_packet( server, packet, len ); +} + diff --git a/reference/hexen2/gameq.txt b/reference/hexen2/gameq.txt index 3f38792..b009862 100644 --- a/reference/hexen2/gameq.txt +++ b/reference/hexen2/gameq.txt @@ -1,52 +1,52 @@ -. - * - * $Id: hexen2.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $ - */ - -[hexen2] -status = "\x80\x00\x00\x0e\x02HEXENII\x00\x05" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Hexen 2 protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_hexen2 extends GameQ_Protocol -{ - /* - * status packet - */ - public function status() - { - // Header? - $this->p->skip(5); - - $this->r->add('address', $this->p->readString()); - $this->r->add('servername', $this->p->readString()); - $this->r->add('map', $this->p->readString()); - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('max_players', $this->p->readInt8()); - $this->p->skip(); // unknown - } -} -?> +. + * + * $Id: hexen2.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $ + */ + +[hexen2] +status = "\x80\x00\x00\x0e\x02HEXENII\x00\x05" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Hexen 2 protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_hexen2 extends GameQ_Protocol +{ + /* + * status packet + */ + public function status() + { + // Header? + $this->p->skip(5); + + $this->r->add('address', $this->p->readString()); + $this->r->add('servername', $this->p->readString()); + $this->r->add('map', $this->p->readString()); + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('max_players', $this->p->readInt8()); + $this->p->skip(); // unknown + } +} +?> diff --git a/reference/openttd/gameq.txt b/reference/openttd/gameq.txt index 3d60f8d..2a11726 100644 --- a/reference/openttd/gameq.txt +++ b/reference/openttd/gameq.txt @@ -1,77 +1,77 @@ -. - * - * $Id: openttd.php,v 1.1 2009/10/24 18:45:16 evilpie Exp $ - */ - -[openttd] -status = "\x03\x00\x00" - - -require_once GAMEQ_BASE . 'Protocol.php'; - -/** - * OpenTTD Protocol, direct port from udp.cpp source from the game - * - * @author Tom Schuster - * @version $Revision: 1.1 $ - */ - - class GameQ_Protocol_openttd extends GameQ_Protocol - { - public function status () - { - $this->p->readInt16(); # packet size - $this->p->readInt8(); # packet type - - $protocol_version = $this->p->readInt8(); - $this->r->add('protocol_version', $protocol_version); - switch ($protocol_version) - { - case 4: - $num_grfs = $this->p->readInt8(); #number of grfs - $this->r->add('num_grfs', $num_grfs); - $this->p->skip ($num_grfs * 20); #skip id and md5 hash - case 3: - $this->r->add('game_date', $this->p->readInt32()); - $this->r->add('start_date', $this->p->readInt32()); - case 2: - $this->r->add('companies_max', $this->p->readInt8()); - $this->r->add('companies_on', $this->p->readInt8()); - $this->r->add('spectators_max', $this->p->readInt8()); - case 1: - $this->r->add('hostname', $this->p->readString()); - $this->r->add('version', $this->p->readString()); - $this->r->add('language', $this->p->readInt8()); - $this->r->add('password', $this->p->readInt8()); - $this->r->add('max_clients', $this->p->readInt8()); - $this->r->add('clients', $this->p->readInt8()); - $this->r->add('spectators', $this->p->readInt8()); - if ($protocol_version < 3) - { - $days = ( 365 * 1920 + 1920 / 4 - 1920 / 100 + 1920 / 400 ); - $this->r->add('game_date', $this->p->readInt16() + $days); - $this->r->add('start_date', $this->p->readInt16() + $days); - } - $this->r->add('map', $this->p->readString()); - $this->r->add('map_width', $this->p->readInt16()); - $this->r->add('map_height', $this->p->readInt16()); - $this->r->add('map_type', $this->p->readInt8()); - $this->r->add('dedicated', $this->p->readInt8()); - } - } +. + * + * $Id: openttd.php,v 1.1 2009/10/24 18:45:16 evilpie Exp $ + */ + +[openttd] +status = "\x03\x00\x00" + + +require_once GAMEQ_BASE . 'Protocol.php'; + +/** + * OpenTTD Protocol, direct port from udp.cpp source from the game + * + * @author Tom Schuster + * @version $Revision: 1.1 $ + */ + + class GameQ_Protocol_openttd extends GameQ_Protocol + { + public function status () + { + $this->p->readInt16(); # packet size + $this->p->readInt8(); # packet type + + $protocol_version = $this->p->readInt8(); + $this->r->add('protocol_version', $protocol_version); + switch ($protocol_version) + { + case 4: + $num_grfs = $this->p->readInt8(); #number of grfs + $this->r->add('num_grfs', $num_grfs); + $this->p->skip ($num_grfs * 20); #skip id and md5 hash + case 3: + $this->r->add('game_date', $this->p->readInt32()); + $this->r->add('start_date', $this->p->readInt32()); + case 2: + $this->r->add('companies_max', $this->p->readInt8()); + $this->r->add('companies_on', $this->p->readInt8()); + $this->r->add('spectators_max', $this->p->readInt8()); + case 1: + $this->r->add('hostname', $this->p->readString()); + $this->r->add('version', $this->p->readString()); + $this->r->add('language', $this->p->readInt8()); + $this->r->add('password', $this->p->readInt8()); + $this->r->add('max_clients', $this->p->readInt8()); + $this->r->add('clients', $this->p->readInt8()); + $this->r->add('spectators', $this->p->readInt8()); + if ($protocol_version < 3) + { + $days = ( 365 * 1920 + 1920 / 4 - 1920 / 100 + 1920 / 400 ); + $this->r->add('game_date', $this->p->readInt16() + $days); + $this->r->add('start_date', $this->p->readInt16() + $days); + } + $this->r->add('map', $this->p->readString()); + $this->r->add('map_width', $this->p->readInt16()); + $this->r->add('map_height', $this->p->readInt16()); + $this->r->add('map_type', $this->p->readInt8()); + $this->r->add('dedicated', $this->p->readInt8()); + } + } } \ No newline at end of file diff --git a/reference/openttd/lgsl.txt b/reference/openttd/lgsl.txt index 296852b..9cd7220 100644 --- a/reference/openttd/lgsl.txt +++ b/reference/openttd/lgsl.txt @@ -1,64 +1,64 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \----------------------------------------------------------------------------------------------------------- - - - - function lgsl_query_22(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ - - fwrite($lgsl_fp,"\x03\x00\x00"); - - $buffer = fread($lgsl_fp, 4096); - $buffer = substr($buffer, 3); // REMOVE HEADER - - if (!$buffer) { return FALSE; } - - $response_type = ord(lgsl_cut_byte($buffer, 1)); // TYPE SHOULD BE 4 - -//---------------------------------------------------------+ - - $grf_count = ord(lgsl_cut_byte($buffer, 1)); - - for ($a=0; $a<$grf_count; $a++) - { - $server['e']['grf_'.$a.'_id'] = strtoupper(dechex(lgsl_unpack(lgsl_cut_byte($buffer, 4), "N"))); - - for ($b=0; $b<16; $b++) - { - $server['e']['grf_'.$a.'_md5'] .= strtoupper(dechex(ord(lgsl_cut_byte($buffer, 1)))); - } - } - -//---------------------------------------------------------+ - - $server['e']['date_current'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L"); - $server['e']['date_start'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L"); - $server['e']['companies_max'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['companies'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['spectators_max'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['name'] = lgsl_cut_string($buffer); - $server['e']['version'] = lgsl_cut_string($buffer); - $server['e']['language'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['map'] = lgsl_cut_string($buffer); - $server['e']['map_width'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['e']['map_height'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['e']['map_set'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); - - // DOES NOT RETURN PLAYER INFORMATION - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \----------------------------------------------------------------------------------------------------------- + + + + function lgsl_query_22(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ + + fwrite($lgsl_fp,"\x03\x00\x00"); + + $buffer = fread($lgsl_fp, 4096); + $buffer = substr($buffer, 3); // REMOVE HEADER + + if (!$buffer) { return FALSE; } + + $response_type = ord(lgsl_cut_byte($buffer, 1)); // TYPE SHOULD BE 4 + +//---------------------------------------------------------+ + + $grf_count = ord(lgsl_cut_byte($buffer, 1)); + + for ($a=0; $a<$grf_count; $a++) + { + $server['e']['grf_'.$a.'_id'] = strtoupper(dechex(lgsl_unpack(lgsl_cut_byte($buffer, 4), "N"))); + + for ($b=0; $b<16; $b++) + { + $server['e']['grf_'.$a.'_md5'] .= strtoupper(dechex(ord(lgsl_cut_byte($buffer, 1)))); + } + } + +//---------------------------------------------------------+ + + $server['e']['date_current'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L"); + $server['e']['date_start'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L"); + $server['e']['companies_max'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['companies'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['spectators_max'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['name'] = lgsl_cut_string($buffer); + $server['e']['version'] = lgsl_cut_string($buffer); + $server['e']['language'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['map'] = lgsl_cut_string($buffer); + $server['e']['map_width'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['e']['map_height'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['e']['map_set'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); + + // DOES NOT RETURN PLAYER INFORMATION + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/openttd/qstat.txt b/reference/openttd/qstat.txt index 9a56ee0..5d582b3 100644 --- a/reference/openttd/qstat.txt +++ b/reference/openttd/qstat.txt @@ -1,404 +1,404 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - -char ottd_serverinfo[] = { - 0x03, 0x00, // packet length - 0x00, // packet type -}; - -char ottd_serverdetails[] = { - 0x03, 0x00, // packet length - 0x02, // packet type -}; - - - { - /* openTTD */ - OTTD_SERVER, /* id */ - "OTTDS", /* type_prefix */ - "ottds", /* type_string */ - "-ottds", /* type_option */ - "OpenTTD", /* game_name */ - 0, /* master */ - 3979, /* default_port */ - 0, /* port_offset */ - 0, /* flags */ - "", /* game_rule */ - "OPENTTD", /* template_var */ - (char*) &ottd_serverinfo, /* status_packet */ - sizeof( ottd_serverinfo), /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - (char*) &ottd_serverdetails,/* rule_packet */ - sizeof( ottd_serverdetails), /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_q2_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_q2_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_player_info,/* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_ottd_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_ottd_packet, /* packet_func */ -}, - - - -/* - * qstat 2.11 - * - * opentTTD protocol - * Copyright 2007 Ludwig Nussel - * - * Licensed under the Artistic License, see LICENSE.txt for license terms - */ - -#include -#ifndef _WIN32 -#include -#endif -#include -#include - -#include "qstat.h" -#include "qserver.h" -#include "debug.h" - -enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 }; - -static const char* vehicle_types[] = { - "num_trains", - "num_trucks", - "num_busses", - "num_aircrafts", - "num_ships", -}; - -static const char* station_types[] = { - "num_stations", - "num_truckbays", - "num_busstations", - "num_airports", - "num_docks", -}; - -query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - unsigned num; - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - server->server_name = MASTER; - - if(swap_short_from_little(rawpkt) != pktlen) - { - malformed_packet( server, "invalid packet length" ); - return PKT_ERROR; - } - if(rawpkt[2] != 7) - { - malformed_packet( server, "invalid packet type" ); - return PKT_ERROR; - } - - if(rawpkt[3] != 1) - { - malformed_packet( server, "invalid packet version" ); - return PKT_ERROR; - } - - num = swap_short_from_little(&rawpkt[4]); - rawpkt += 6; - pktlen -= 6; - if( num && num*6 <= pktlen ) - { - unsigned i; - server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen ); - memset(server->master_pkt + server->master_pkt_len, 0, pktlen ); - server->master_pkt_len += pktlen; - for( i = 0; i < num * 6; i += 6 ) - { - memcpy(&server->master_pkt[i], &rawpkt[i], 4); - server->master_pkt[i+4] = rawpkt[i+5]; - server->master_pkt[i+5] = rawpkt[i+4]; - } - server->n_servers += num; - } - else - { - malformed_packet( server, "invalid packet" ); - return PKT_ERROR; - } - - bind_sockets(); - - return DONE_AUTO; -} - -#define xstr(s) str(s) -#define str(s) #s - -#define GET_STRING do { \ - str = (char*)ptr; \ - ptr = memchr(ptr, '\0', end-ptr); \ - if ( !ptr ) \ - { \ - malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \ - return PKT_ERROR; \ - } \ - ++ptr; \ - } while(0) - -#define FAIL_IF(cond, msg) \ - if((cond)) { \ - malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \ - return PKT_ERROR; \ - } - -#define INVALID_IF(cond) \ - FAIL_IF(cond, "invalid packet") - -query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - unsigned char *ptr = (unsigned char*)rawpkt; - unsigned char *end = (unsigned char*)(rawpkt + pktlen); - unsigned char type; - char* str; - char buf[32]; - unsigned ver; - - server->n_servers++; - if ( server->server_name == NULL) - { - server->ping_total += time_delta( &packet_recv_time, &server->packet_time1); - server->n_requests++; - } - else - { - gettimeofday( &server->packet_time1, NULL); - } - - FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet"); - - type = ptr[2]; - ver = ptr[3]; - ptr += 4; - - debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver); - - FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported"); - - if(type == 1) // info packet - { - unsigned numgrf = *ptr; - FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number"); - ptr += numgrf * 20 + 1; - - snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); - add_rule(server, "date_days", buf, NO_FLAGS); - ptr += 4; - - snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); - add_rule(server, "startdate_days", buf, NO_FLAGS); - ptr += 4; - - FAIL_IF(ptr + 3 > end, "invalid packet"); - - snprintf(buf, sizeof(buf), "%hhu", ptr[0]); - add_rule(server, "maxcompanies", buf, NO_FLAGS); - snprintf(buf, sizeof(buf), "%hhu", ptr[1]); - add_rule(server, "numcompanies", buf, NO_FLAGS); - server->max_spectators = ptr[2]; - ptr += 3; - - GET_STRING; - server->server_name = strdup(str); - - GET_STRING; - add_rule(server, "version", str, NO_FLAGS); - - FAIL_IF(ptr + 7 > end, "invalid packet"); - - { - static const char* langs[] = { - "any", - "English", - "German", - "French" - }; - unsigned i = *ptr++; - if(i > 3) i = 0; - add_rule(server, "language", (char*)langs[i], NO_FLAGS); - } - - add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS); - - server->max_players = *ptr++; - server->num_players = *ptr++; - server->num_spectators = *ptr++; - - GET_STRING; - - server->map_name = strdup(str); - - snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); - add_rule(server, "map_width", buf, NO_FLAGS); - snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); - add_rule(server, "map_height", buf, NO_FLAGS); - - { - static const char* sets[] = { - "temperate", - "arctic", - "desert", - "toyland" - }; - unsigned i = *ptr++; - if(i > 3) i = 0; - add_rule(server, "map_set", (char*)sets[i], NO_FLAGS); - } - - add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS); - } - else if(type == 3) // player packet - { - unsigned i, j; - INVALID_IF(ptr + 2 > end); - - server->num_players = *ptr++; - - for(i = 0; i < server->num_players; ++i) - { - unsigned long long lli; - struct player* player; - unsigned char nr; - - nr = *ptr++; - - debug(3, "player number %d", nr); - player = add_player(server, i); - FAIL_IF(!player, "can't allocate player"); - - GET_STRING; - player->name = strdup(str); - debug(3, "name %s", str); - player->frags = 0; - - INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end); - - snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); - player_add_info(player, "startdate", buf, 0); - ptr += 4; - - lli = swap_long_from_little(ptr+4); - lli <<= 32; - lli += swap_long_from_little(ptr); - snprintf(buf, sizeof(buf), "%lld", lli); - player_add_info(player, "value", buf, 0); - ptr += 8; - - lli = swap_long_from_little(ptr+4); - lli <<= 32; - lli = swap_long_from_little(ptr); - snprintf(buf, sizeof(buf), "%lld", lli); - player_add_info(player, "money", buf, 0); - ptr += 8; - - lli = swap_long_from_little(ptr+4); - lli <<= 32; - lli += swap_long_from_little(ptr); - snprintf(buf, sizeof(buf), "%lld", lli); - player_add_info(player, "income", buf, 0); - ptr += 8; - - snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); - player_add_info(player, "performance", buf, 0); - ptr += 2; - - player_add_info(player, "password", *ptr?"1":"0", 0); - ++ptr; - - for (j = 0; j < MAX_VEHICLE_TYPES; ++j) - { - snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); - player_add_info(player, (char*)vehicle_types[j], buf, 0); - ptr += 2; - } - for (j = 0; j < MAX_STATION_TYPES; ++j) - { - snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); - player_add_info(player, (char*)station_types[j], buf, 0); - ptr += 2; - } - - if (ver != 5) - { - // connections - while(ptr + 1 < end && *ptr) - { - ++ptr; - GET_STRING; // client name - debug(3, "%s played by %s", str, player->name); - GET_STRING; // id - INVALID_IF(ptr + 4 > end); - ptr += 4; - } - - ++ptr; // record terminated by zero byte - } - } - - // spectators - while(ptr + 1 < end && *ptr) - { - ++ptr; - GET_STRING; // client name - debug(3, "spectator %s", str); - GET_STRING; // id - INVALID_IF(ptr + 4 > end); - ptr += 4; - } - ++ptr; // record terminated by zero byte - - server->next_rule = NO_SERVER_RULES; // we're done - server->next_player_info = server->num_players; // we're done - } - else - { - malformed_packet( server, "invalid type" ); - return PKT_ERROR; - } - - server->retry1 = n_retries; // we're done with this packet, reset retry counter - - return DONE_AUTO; -} - -query_status_t send_ottdmaster_request_packet(struct qserver *server) -{ - return qserver_send_initial(server, server->type->master_packet, server->type->master_len); -} - -query_status_t send_ottd_request_packet(struct qserver *server) -{ - qserver_send_initial(server, server->type->status_packet, server->type->status_len); - - if(get_server_rules || get_player_info) - { - server->next_rule = ""; // trigger calling send_a2s_rule_request_packet - } - - return INPROGRESS; -} +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + +char ottd_serverinfo[] = { + 0x03, 0x00, // packet length + 0x00, // packet type +}; + +char ottd_serverdetails[] = { + 0x03, 0x00, // packet length + 0x02, // packet type +}; + + + { + /* openTTD */ + OTTD_SERVER, /* id */ + "OTTDS", /* type_prefix */ + "ottds", /* type_string */ + "-ottds", /* type_option */ + "OpenTTD", /* game_name */ + 0, /* master */ + 3979, /* default_port */ + 0, /* port_offset */ + 0, /* flags */ + "", /* game_rule */ + "OPENTTD", /* template_var */ + (char*) &ottd_serverinfo, /* status_packet */ + sizeof( ottd_serverinfo), /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + (char*) &ottd_serverdetails,/* rule_packet */ + sizeof( ottd_serverdetails), /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_q2_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_q2_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_player_info,/* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_ottd_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_ottd_packet, /* packet_func */ +}, + + + +/* + * qstat 2.11 + * + * opentTTD protocol + * Copyright 2007 Ludwig Nussel + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + */ + +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#include "qstat.h" +#include "qserver.h" +#include "debug.h" + +enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 }; + +static const char* vehicle_types[] = { + "num_trains", + "num_trucks", + "num_busses", + "num_aircrafts", + "num_ships", +}; + +static const char* station_types[] = { + "num_stations", + "num_truckbays", + "num_busstations", + "num_airports", + "num_docks", +}; + +query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + unsigned num; + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + server->server_name = MASTER; + + if(swap_short_from_little(rawpkt) != pktlen) + { + malformed_packet( server, "invalid packet length" ); + return PKT_ERROR; + } + if(rawpkt[2] != 7) + { + malformed_packet( server, "invalid packet type" ); + return PKT_ERROR; + } + + if(rawpkt[3] != 1) + { + malformed_packet( server, "invalid packet version" ); + return PKT_ERROR; + } + + num = swap_short_from_little(&rawpkt[4]); + rawpkt += 6; + pktlen -= 6; + if( num && num*6 <= pktlen ) + { + unsigned i; + server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen ); + memset(server->master_pkt + server->master_pkt_len, 0, pktlen ); + server->master_pkt_len += pktlen; + for( i = 0; i < num * 6; i += 6 ) + { + memcpy(&server->master_pkt[i], &rawpkt[i], 4); + server->master_pkt[i+4] = rawpkt[i+5]; + server->master_pkt[i+5] = rawpkt[i+4]; + } + server->n_servers += num; + } + else + { + malformed_packet( server, "invalid packet" ); + return PKT_ERROR; + } + + bind_sockets(); + + return DONE_AUTO; +} + +#define xstr(s) str(s) +#define str(s) #s + +#define GET_STRING do { \ + str = (char*)ptr; \ + ptr = memchr(ptr, '\0', end-ptr); \ + if ( !ptr ) \ + { \ + malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \ + return PKT_ERROR; \ + } \ + ++ptr; \ + } while(0) + +#define FAIL_IF(cond, msg) \ + if((cond)) { \ + malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \ + return PKT_ERROR; \ + } + +#define INVALID_IF(cond) \ + FAIL_IF(cond, "invalid packet") + +query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + unsigned char *ptr = (unsigned char*)rawpkt; + unsigned char *end = (unsigned char*)(rawpkt + pktlen); + unsigned char type; + char* str; + char buf[32]; + unsigned ver; + + server->n_servers++; + if ( server->server_name == NULL) + { + server->ping_total += time_delta( &packet_recv_time, &server->packet_time1); + server->n_requests++; + } + else + { + gettimeofday( &server->packet_time1, NULL); + } + + FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet"); + + type = ptr[2]; + ver = ptr[3]; + ptr += 4; + + debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver); + + FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported"); + + if(type == 1) // info packet + { + unsigned numgrf = *ptr; + FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number"); + ptr += numgrf * 20 + 1; + + snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); + add_rule(server, "date_days", buf, NO_FLAGS); + ptr += 4; + + snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); + add_rule(server, "startdate_days", buf, NO_FLAGS); + ptr += 4; + + FAIL_IF(ptr + 3 > end, "invalid packet"); + + snprintf(buf, sizeof(buf), "%hhu", ptr[0]); + add_rule(server, "maxcompanies", buf, NO_FLAGS); + snprintf(buf, sizeof(buf), "%hhu", ptr[1]); + add_rule(server, "numcompanies", buf, NO_FLAGS); + server->max_spectators = ptr[2]; + ptr += 3; + + GET_STRING; + server->server_name = strdup(str); + + GET_STRING; + add_rule(server, "version", str, NO_FLAGS); + + FAIL_IF(ptr + 7 > end, "invalid packet"); + + { + static const char* langs[] = { + "any", + "English", + "German", + "French" + }; + unsigned i = *ptr++; + if(i > 3) i = 0; + add_rule(server, "language", (char*)langs[i], NO_FLAGS); + } + + add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS); + + server->max_players = *ptr++; + server->num_players = *ptr++; + server->num_spectators = *ptr++; + + GET_STRING; + + server->map_name = strdup(str); + + snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); + add_rule(server, "map_width", buf, NO_FLAGS); + snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); + add_rule(server, "map_height", buf, NO_FLAGS); + + { + static const char* sets[] = { + "temperate", + "arctic", + "desert", + "toyland" + }; + unsigned i = *ptr++; + if(i > 3) i = 0; + add_rule(server, "map_set", (char*)sets[i], NO_FLAGS); + } + + add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS); + } + else if(type == 3) // player packet + { + unsigned i, j; + INVALID_IF(ptr + 2 > end); + + server->num_players = *ptr++; + + for(i = 0; i < server->num_players; ++i) + { + unsigned long long lli; + struct player* player; + unsigned char nr; + + nr = *ptr++; + + debug(3, "player number %d", nr); + player = add_player(server, i); + FAIL_IF(!player, "can't allocate player"); + + GET_STRING; + player->name = strdup(str); + debug(3, "name %s", str); + player->frags = 0; + + INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end); + + snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); + player_add_info(player, "startdate", buf, 0); + ptr += 4; + + lli = swap_long_from_little(ptr+4); + lli <<= 32; + lli += swap_long_from_little(ptr); + snprintf(buf, sizeof(buf), "%lld", lli); + player_add_info(player, "value", buf, 0); + ptr += 8; + + lli = swap_long_from_little(ptr+4); + lli <<= 32; + lli = swap_long_from_little(ptr); + snprintf(buf, sizeof(buf), "%lld", lli); + player_add_info(player, "money", buf, 0); + ptr += 8; + + lli = swap_long_from_little(ptr+4); + lli <<= 32; + lli += swap_long_from_little(ptr); + snprintf(buf, sizeof(buf), "%lld", lli); + player_add_info(player, "income", buf, 0); + ptr += 8; + + snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); + player_add_info(player, "performance", buf, 0); + ptr += 2; + + player_add_info(player, "password", *ptr?"1":"0", 0); + ++ptr; + + for (j = 0; j < MAX_VEHICLE_TYPES; ++j) + { + snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); + player_add_info(player, (char*)vehicle_types[j], buf, 0); + ptr += 2; + } + for (j = 0; j < MAX_STATION_TYPES; ++j) + { + snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); + player_add_info(player, (char*)station_types[j], buf, 0); + ptr += 2; + } + + if (ver != 5) + { + // connections + while(ptr + 1 < end && *ptr) + { + ++ptr; + GET_STRING; // client name + debug(3, "%s played by %s", str, player->name); + GET_STRING; // id + INVALID_IF(ptr + 4 > end); + ptr += 4; + } + + ++ptr; // record terminated by zero byte + } + } + + // spectators + while(ptr + 1 < end && *ptr) + { + ++ptr; + GET_STRING; // client name + debug(3, "spectator %s", str); + GET_STRING; // id + INVALID_IF(ptr + 4 > end); + ptr += 4; + } + ++ptr; // record terminated by zero byte + + server->next_rule = NO_SERVER_RULES; // we're done + server->next_player_info = server->num_players; // we're done + } + else + { + malformed_packet( server, "invalid type" ); + return PKT_ERROR; + } + + server->retry1 = n_retries; // we're done with this packet, reset retry counter + + return DONE_AUTO; +} + +query_status_t send_ottdmaster_request_packet(struct qserver *server) +{ + return qserver_send_initial(server, server->type->master_packet, server->type->master_len); +} + +query_status_t send_ottd_request_packet(struct qserver *server) +{ + qserver_send_initial(server, server->type->status_packet, server->type->status_len); + + if(get_server_rules || get_player_info) + { + server->next_rule = ""; // trigger calling send_a2s_rule_request_packet + } + + return INPROGRESS; +} diff --git a/reference/plainsight/lgsl.txt b/reference/plainsight/lgsl.txt index 6f994e4..a727e87 100644 --- a/reference/plainsight/lgsl.txt +++ b/reference/plainsight/lgsl.txt @@ -1,33 +1,33 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_32(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ - - fwrite($lgsl_fp, "\x05\x00\x00\x01\x0A"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - - $buffer = substr($buffer, 5); // REMOVE HEADER - - $server['s']['name'] = lgsl_cut_pascal($buffer); - $server['s']['map'] = lgsl_cut_pascal($buffer); - $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['playersmax'] = 0; // HELD ON MASTER - - // DOES NOT RETURN PLAYER INFORMATION - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + function lgsl_query_32(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ + + fwrite($lgsl_fp, "\x05\x00\x00\x01\x0A"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + + $buffer = substr($buffer, 5); // REMOVE HEADER + + $server['s']['name'] = lgsl_cut_pascal($buffer); + $server['s']['map'] = lgsl_cut_pascal($buffer); + $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['playersmax'] = 0; // HELD ON MASTER + + // DOES NOT RETURN PLAYER INFORMATION + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/ravenshield/gamedignote.txt b/reference/ravenshield/gamedignote.txt index 07e0eda..f0e43b8 100644 --- a/reference/ravenshield/gamedignote.txt +++ b/reference/ravenshield/gamedignote.txt @@ -1,3 +1,3 @@ -Most sources say raven shield uses gamespy1, but these docs are for -some other raven shield protocol. If anyone has an issue, this may -be the actual protocol. +Most sources say raven shield uses gamespy1, but these docs are for +some other raven shield protocol. If anyone has an issue, this may +be the actual protocol. diff --git a/reference/ravenshield/qstat.txt b/reference/ravenshield/qstat.txt index 5e5addd..f6ce066 100644 --- a/reference/ravenshield/qstat.txt +++ b/reference/ravenshield/qstat.txt @@ -1,446 +1,446 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - -char ravenshield_serverquery[] = "REPORT"; - - -{ - /* RAVENSHIELD PROTOCOL */ - RAVENSHIELD_SERVER, /* id */ - "RSS", /* type_prefix */ - "rss", /* type_string */ - "-rss", /* type_option */ - "Ravenshield", /* game_name */ - 0, /* master */ - RAVENSHIELD_DEFAULT_PORT, /* default_port */ - 1000, /* port_offset */ - TF_QUERY_ARG, /* flags */ - "gametype", /* game_rule */ - "RAVENSHIELD", /* template_var */ - (char*)ravenshield_serverquery, /* status_packet */ - sizeof( ravenshield_serverquery ) - 1, /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_ravenshield_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_ravenshield_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_ravenshield_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_qserver_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_ravenshield_packet, /* packet_func */ -}, - - - - -query_status_t deal_with_ravenshield_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - char *s, *key, *value; - - debug( 2, "deal_with_ravenshield_packet %p, %d", server, pktlen ); - - server->n_servers++; - if (NULL == server->server_name) - { - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - } - else - { - gettimeofday(&server->packet_time1, NULL); - } - - rawpkt[pktlen] = '\0'; - - s = rawpkt; - while (*s) - { - // Find the seperator - while (*s && *s != '\xB6') - { - s++; - } - - if (! *s) - { - // Hit the end no more - break; - } - - // key start - key = ++s; - while (*s && *s != ' ') - { - s++; - } - if (*s != ' ') - { - // malformed - break; - } - *s++ = '\0'; - // key end - // value start - value = s; - - while (*s && *s != '\xB6') - { - s++; - } - - if (*s == '\xB6') - { - *(s - 1) = '\0'; - } - - // Decode current key par - if (0 == strcmp("A1", key)) - { - // Max players - server->max_players = atoi(value); - } - else if (0 == strcmp("A2", key)) - { - // TeamKillerPenalty - add_rule(server, "TeamKillerPenalty", value, NO_FLAGS); - } - else if (0 == strcmp("B1", key)) - { - // Current players - server->num_players = atoi(value); - } - else if (0 == strcmp("B2", key)) - { - // AllowRadar - add_rule(server, "AllowRadar", value, NO_FLAGS); - } - else if (0 == strcmp("D2", key)) - { - // Version info - add_rule(server, "Version", value, NO_FLAGS); - } - else if (0 == strcmp("E1", key)) - { - // Current map - server->map_name = strdup(value); - } - else if (0 == strcmp("E2", key)) - { - // Unknown - } - else if (0 == strcmp("F1", key)) - { - // Game type - server->game = find_ravenshield_game(value); - add_rule(server, server->type->game_rule, server->game, NO_FLAGS); - } - else if (0 == strcmp("F2", key)) - { - // Unknown - } - else if (0 == strcmp("G1", key)) - { - // Password - add_rule(server, "Password", value, NO_FLAGS); - } - else if (0 == strcmp("G2", key)) - { - // Query port - } - else if (0 == strcmp("H1", key)) - { - // Unknown - } - else if (0 == strcmp("H2", key)) - { - // Number of Terrorists - add_rule(server, "nbTerro", value, NO_FLAGS); - } - else if (0 == strcmp("I1", key)) - { - // Server name - server->server_name = strdup(value); - } - else if (0 == strcmp("I2", key)) - { - // Unknown - } - else if (0 == strcmp("J1", key)) - { - // Game Type Order - // Not pretty ignore for now - //add_rule( server, "Game Type Order", value, NO_FLAGS ); - } - else if (0 == strcmp("J2", key)) - { - // RotateMap - add_rule(server, "RotateMap", value, NO_FLAGS); - } - else if (0 == strcmp("K1", key)) - { - // Map Cycle - // Not pretty ignore for now - //add_rule( server, "Map Cycle", value, NO_FLAGS ); - } - else if (0 == strcmp("K2", key)) - { - // Force First Person Weapon - add_rule(server, "ForceFPersonWeapon", value, NO_FLAGS); - } - else if (0 == strcmp("L1", key)) - { - // Players names - int player_number = 0; - char *n = value; - - if (*n == '/') - { - // atleast 1 player - n++; - while (*n && *n != '\xB6') - { - char *player_name = n; - while (*n && *n != '/' && *n != '\xB6') - { - n++; - } - - if (*n == '/') - { - *n++ = '\0'; - } - else if (*n == '\xB6') - { - *(n - 1) = '\0'; - } - - if (0 != strlen(player_name)) - { - struct player *player = add_player(server, player_number); - if (NULL != player) - { - player->name = strdup(player_name); - } - player_number++; - } - } - } - } - else if (0 == strcmp("L3", key)) - { - // PunkBuster state - add_rule(server, "PunkBuster", value, NO_FLAGS); - } - else if (0 == strcmp("M1", key)) - { - // Players times - int player_number = 0; - char *n = value; - if (*n == '/') - { - // atleast 1 player - n++; - while (*n && *n != '\xB6') - { - char *time = n; - while (*n && *n != '/' && *n != '\xB6') - { - n++; - } - - if (*n == '/') - { - *n++ = '\0'; - } - else if (*n == '\xB6') - { - *(n - 1) = '\0'; - } - - if (0 != strlen(time)) - { - int mins, seconds; - if (2 == sscanf(time, "%d:%d", &mins, &seconds)) - { - struct player *player = get_player_by_number(server, player_number); - if (NULL != player) - { - player->connect_time = mins * 60+seconds; - } - } - player_number++; - } - } - } - } - else if (0 == strcmp("N1", key)) - { - // Players ping - int player_number = 0; - char *n = value; - if (*n == '/') - { - // atleast 1 player - n++; - while (*n && *n != '\xB6') - { - char *ping = n; - while (*n && *n != '/' && *n != '\xB6') - { - n++; - } - - if (*n == '/') - { - *n++ = '\0'; - } - else if (*n == '\xB6') - { - *(n - 1) = '\0'; - } - - if (0 != strlen(ping)) - { - struct player *player = get_player_by_number(server, player_number); - if (NULL != player) - { - player->ping = atoi(ping); - } player_number++; - } - } - } - } - else if (0 == strcmp("O1", key)) - { - // Players fags - int player_number = 0; - char *n = value; - if (*n == '/') - { - // atleast 1 player - n++; - while (*n && *n != '\xB6') - { - char *frags = n; - while (*n && *n != '/' && *n != '\xB6') - { - n++; - } - - if (*n == '/') - { - *n++ = '\0'; - } - else if (*n == '\xB6') - { - *(n - 1) = '\0'; - } - - if (0 != strlen(frags)) - { - struct player *player = get_player_by_number(server, player_number); - if (NULL != player) - { - player->frags = atoi(frags); - } player_number++; - } - } - } - } - else if (0 == strcmp("P1", key)) - { - // Game port - // Not pretty ignore for now - /* - change_server_port( server, atoi( value ), 0 ); - */ - } - else if (0 == strcmp("Q1", key)) - { - // RoundsPerMatch - add_rule(server, "RoundsPerMatch", value, NO_FLAGS); - } - else if (0 == strcmp("R1", key)) - { - // RoundTime - add_rule(server, "RoundTime", value, NO_FLAGS); - } - else if (0 == strcmp("S1", key)) - { - // BetweenRoundTime - add_rule(server, "BetweenRoundTime", value, NO_FLAGS); - } - else if (0 == strcmp("T1", key)) - { - // BombTime - add_rule(server, "BombTime", value, NO_FLAGS); - } - else if (0 == strcmp("W1", key)) - { - // ShowNames - add_rule(server, "ShowNames", value, NO_FLAGS); - } - else if (0 == strcmp("X1", key)) - { - // InternetServer - add_rule(server, "InternetServer", value, NO_FLAGS); - } - else if (0 == strcmp("Y1", key)) - { - // FriendlyFire - add_rule(server, "FriendlyFire", value, NO_FLAGS); - } - else if (0 == strcmp("Z1", key)) - { - // Autobalance - add_rule(server, "Autobalance", value, NO_FLAGS); - } - } - - return DONE_FORCE; -} - - -char *find_ravenshield_game(char *gameno) -{ - switch (atoi(gameno)) - { - case 8: - return strdup("Team Deathmatch"); - break; - case 13: - return strdup("Deathmatch"); - break; - case 14: - return strdup("Team Deathmatch"); - break; - case 15: - return strdup("Bomb"); - break; - case 16: - return strdup("Escort Pilot"); - break; - default: - // 1.50 and above actually uses a string so - // return that - return strdup(gameno); - break; - } -} +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + +char ravenshield_serverquery[] = "REPORT"; + + +{ + /* RAVENSHIELD PROTOCOL */ + RAVENSHIELD_SERVER, /* id */ + "RSS", /* type_prefix */ + "rss", /* type_string */ + "-rss", /* type_option */ + "Ravenshield", /* game_name */ + 0, /* master */ + RAVENSHIELD_DEFAULT_PORT, /* default_port */ + 1000, /* port_offset */ + TF_QUERY_ARG, /* flags */ + "gametype", /* game_rule */ + "RAVENSHIELD", /* template_var */ + (char*)ravenshield_serverquery, /* status_packet */ + sizeof( ravenshield_serverquery ) - 1, /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_ravenshield_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_ravenshield_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_ravenshield_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_qserver_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_ravenshield_packet, /* packet_func */ +}, + + + + +query_status_t deal_with_ravenshield_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + char *s, *key, *value; + + debug( 2, "deal_with_ravenshield_packet %p, %d", server, pktlen ); + + server->n_servers++; + if (NULL == server->server_name) + { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } + else + { + gettimeofday(&server->packet_time1, NULL); + } + + rawpkt[pktlen] = '\0'; + + s = rawpkt; + while (*s) + { + // Find the seperator + while (*s && *s != '\xB6') + { + s++; + } + + if (! *s) + { + // Hit the end no more + break; + } + + // key start + key = ++s; + while (*s && *s != ' ') + { + s++; + } + if (*s != ' ') + { + // malformed + break; + } + *s++ = '\0'; + // key end + // value start + value = s; + + while (*s && *s != '\xB6') + { + s++; + } + + if (*s == '\xB6') + { + *(s - 1) = '\0'; + } + + // Decode current key par + if (0 == strcmp("A1", key)) + { + // Max players + server->max_players = atoi(value); + } + else if (0 == strcmp("A2", key)) + { + // TeamKillerPenalty + add_rule(server, "TeamKillerPenalty", value, NO_FLAGS); + } + else if (0 == strcmp("B1", key)) + { + // Current players + server->num_players = atoi(value); + } + else if (0 == strcmp("B2", key)) + { + // AllowRadar + add_rule(server, "AllowRadar", value, NO_FLAGS); + } + else if (0 == strcmp("D2", key)) + { + // Version info + add_rule(server, "Version", value, NO_FLAGS); + } + else if (0 == strcmp("E1", key)) + { + // Current map + server->map_name = strdup(value); + } + else if (0 == strcmp("E2", key)) + { + // Unknown + } + else if (0 == strcmp("F1", key)) + { + // Game type + server->game = find_ravenshield_game(value); + add_rule(server, server->type->game_rule, server->game, NO_FLAGS); + } + else if (0 == strcmp("F2", key)) + { + // Unknown + } + else if (0 == strcmp("G1", key)) + { + // Password + add_rule(server, "Password", value, NO_FLAGS); + } + else if (0 == strcmp("G2", key)) + { + // Query port + } + else if (0 == strcmp("H1", key)) + { + // Unknown + } + else if (0 == strcmp("H2", key)) + { + // Number of Terrorists + add_rule(server, "nbTerro", value, NO_FLAGS); + } + else if (0 == strcmp("I1", key)) + { + // Server name + server->server_name = strdup(value); + } + else if (0 == strcmp("I2", key)) + { + // Unknown + } + else if (0 == strcmp("J1", key)) + { + // Game Type Order + // Not pretty ignore for now + //add_rule( server, "Game Type Order", value, NO_FLAGS ); + } + else if (0 == strcmp("J2", key)) + { + // RotateMap + add_rule(server, "RotateMap", value, NO_FLAGS); + } + else if (0 == strcmp("K1", key)) + { + // Map Cycle + // Not pretty ignore for now + //add_rule( server, "Map Cycle", value, NO_FLAGS ); + } + else if (0 == strcmp("K2", key)) + { + // Force First Person Weapon + add_rule(server, "ForceFPersonWeapon", value, NO_FLAGS); + } + else if (0 == strcmp("L1", key)) + { + // Players names + int player_number = 0; + char *n = value; + + if (*n == '/') + { + // atleast 1 player + n++; + while (*n && *n != '\xB6') + { + char *player_name = n; + while (*n && *n != '/' && *n != '\xB6') + { + n++; + } + + if (*n == '/') + { + *n++ = '\0'; + } + else if (*n == '\xB6') + { + *(n - 1) = '\0'; + } + + if (0 != strlen(player_name)) + { + struct player *player = add_player(server, player_number); + if (NULL != player) + { + player->name = strdup(player_name); + } + player_number++; + } + } + } + } + else if (0 == strcmp("L3", key)) + { + // PunkBuster state + add_rule(server, "PunkBuster", value, NO_FLAGS); + } + else if (0 == strcmp("M1", key)) + { + // Players times + int player_number = 0; + char *n = value; + if (*n == '/') + { + // atleast 1 player + n++; + while (*n && *n != '\xB6') + { + char *time = n; + while (*n && *n != '/' && *n != '\xB6') + { + n++; + } + + if (*n == '/') + { + *n++ = '\0'; + } + else if (*n == '\xB6') + { + *(n - 1) = '\0'; + } + + if (0 != strlen(time)) + { + int mins, seconds; + if (2 == sscanf(time, "%d:%d", &mins, &seconds)) + { + struct player *player = get_player_by_number(server, player_number); + if (NULL != player) + { + player->connect_time = mins * 60+seconds; + } + } + player_number++; + } + } + } + } + else if (0 == strcmp("N1", key)) + { + // Players ping + int player_number = 0; + char *n = value; + if (*n == '/') + { + // atleast 1 player + n++; + while (*n && *n != '\xB6') + { + char *ping = n; + while (*n && *n != '/' && *n != '\xB6') + { + n++; + } + + if (*n == '/') + { + *n++ = '\0'; + } + else if (*n == '\xB6') + { + *(n - 1) = '\0'; + } + + if (0 != strlen(ping)) + { + struct player *player = get_player_by_number(server, player_number); + if (NULL != player) + { + player->ping = atoi(ping); + } player_number++; + } + } + } + } + else if (0 == strcmp("O1", key)) + { + // Players fags + int player_number = 0; + char *n = value; + if (*n == '/') + { + // atleast 1 player + n++; + while (*n && *n != '\xB6') + { + char *frags = n; + while (*n && *n != '/' && *n != '\xB6') + { + n++; + } + + if (*n == '/') + { + *n++ = '\0'; + } + else if (*n == '\xB6') + { + *(n - 1) = '\0'; + } + + if (0 != strlen(frags)) + { + struct player *player = get_player_by_number(server, player_number); + if (NULL != player) + { + player->frags = atoi(frags); + } player_number++; + } + } + } + } + else if (0 == strcmp("P1", key)) + { + // Game port + // Not pretty ignore for now + /* + change_server_port( server, atoi( value ), 0 ); + */ + } + else if (0 == strcmp("Q1", key)) + { + // RoundsPerMatch + add_rule(server, "RoundsPerMatch", value, NO_FLAGS); + } + else if (0 == strcmp("R1", key)) + { + // RoundTime + add_rule(server, "RoundTime", value, NO_FLAGS); + } + else if (0 == strcmp("S1", key)) + { + // BetweenRoundTime + add_rule(server, "BetweenRoundTime", value, NO_FLAGS); + } + else if (0 == strcmp("T1", key)) + { + // BombTime + add_rule(server, "BombTime", value, NO_FLAGS); + } + else if (0 == strcmp("W1", key)) + { + // ShowNames + add_rule(server, "ShowNames", value, NO_FLAGS); + } + else if (0 == strcmp("X1", key)) + { + // InternetServer + add_rule(server, "InternetServer", value, NO_FLAGS); + } + else if (0 == strcmp("Y1", key)) + { + // FriendlyFire + add_rule(server, "FriendlyFire", value, NO_FLAGS); + } + else if (0 == strcmp("Z1", key)) + { + // Autobalance + add_rule(server, "Autobalance", value, NO_FLAGS); + } + } + + return DONE_FORCE; +} + + +char *find_ravenshield_game(char *gameno) +{ + switch (atoi(gameno)) + { + case 8: + return strdup("Team Deathmatch"); + break; + case 13: + return strdup("Deathmatch"); + break; + case 14: + return strdup("Team Deathmatch"); + break; + case 15: + return strdup("Bomb"); + break; + case 16: + return strdup("Escort Pilot"); + break; + default: + // 1.50 and above actually uses a string so + // return that + return strdup(gameno); + break; + } +} diff --git a/reference/redfaction/gameq.txt b/reference/redfaction/gameq.txt index 82b696e..914720c 100644 --- a/reference/redfaction/gameq.txt +++ b/reference/redfaction/gameq.txt @@ -1,59 +1,59 @@ -. - * - * $Id: redfaction.php,v 1.1 2007/06/30 12:43:43 tombuskens Exp $ - */ - -[redfaction] -status = "\x00\x00\x00\x00" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Red Faction Protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_redfaction extends GameQ_Protocol -{ - /* - * getstatus packet - */ - public function status() - { - // Header, we're being carefull here - if ($this->p->read() !== "\x00") { - throw new GameQ_ParsingException($this->p); - } - - // Dunno - while ($this->p->read() !== "\x00") {} - $this->p->read(); - - // Data - $this->r->add('servername', $this->p->readString()); - $this->r->add('gametype', $this->p->readInt8()); - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('max_players', $this->p->readInt8()); - $this->r->add('map', $this->p->readString()); - $this->p->read(); - $this->r->add('dedicated', $this->p->readInt8()); - } -} +. + * + * $Id: redfaction.php,v 1.1 2007/06/30 12:43:43 tombuskens Exp $ + */ + +[redfaction] +status = "\x00\x00\x00\x00" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Red Faction Protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_redfaction extends GameQ_Protocol +{ + /* + * getstatus packet + */ + public function status() + { + // Header, we're being carefull here + if ($this->p->read() !== "\x00") { + throw new GameQ_ParsingException($this->p); + } + + // Dunno + while ($this->p->read() !== "\x00") {} + $this->p->read(); + + // Data + $this->r->add('servername', $this->p->readString()); + $this->r->add('gametype', $this->p->readInt8()); + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('max_players', $this->p->readInt8()); + $this->r->add('map', $this->p->readString()); + $this->p->read(); + $this->r->add('dedicated', $this->p->readInt8()); + } +} diff --git a/reference/rfactor/gameq.txt b/reference/rfactor/gameq.txt index 202c65d..e846281 100644 --- a/reference/rfactor/gameq.txt +++ b/reference/rfactor/gameq.txt @@ -1,62 +1,62 @@ -. - * - * $Id: rfactor.php,v 1.2 2009/08/13 20:46:40 evilpie Exp $ - */ - -[rfactor] -status = "rF_S" - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * rFactor Protocol - * - * @author Tom Buskens - * @version $Revision: 1.2 $ - */ -class GameQ_Protocol_rfactor extends GameQ_Protocol -{ - - public function status() - { - // Header - $this->p->jumpto(17); - $this->r->add('version', $this->p->readInt16()); - $this->p->jumpto(25); - $this->r->add('series', $this->p->readString()); - $this->p->jumpto(45); - $this->r->add('servername', $this->p->readString()); - - $this->p->jumpto(73); - $this->r->add('map', $this->p->readString()); - $this->p->jumpto(105); - $this->r->add('motd', $this->p->readString()); - $this->p->jumpto(206); - $this->r->add('rate', $this->p->readInt8()); - $this->r->add('numplayers', $this->p->readInt8()); - $this->r->add('maxplayers', $this->p->readInt8()); - $this->r->add('numbots', $this->p->readInt8()); - $this->r->add('session', $this->p->readInt8() >> 5); - $this->r->add('damage', $this->p->readInt8()); - $this->p->jumpto(217); - $this->r->add('time', $this->p->readInt16()); - $this->r->add('laps', $this->p->readInt16() / 16); - } -} -?> +. + * + * $Id: rfactor.php,v 1.2 2009/08/13 20:46:40 evilpie Exp $ + */ + +[rfactor] +status = "rF_S" + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * rFactor Protocol + * + * @author Tom Buskens + * @version $Revision: 1.2 $ + */ +class GameQ_Protocol_rfactor extends GameQ_Protocol +{ + + public function status() + { + // Header + $this->p->jumpto(17); + $this->r->add('version', $this->p->readInt16()); + $this->p->jumpto(25); + $this->r->add('series', $this->p->readString()); + $this->p->jumpto(45); + $this->r->add('servername', $this->p->readString()); + + $this->p->jumpto(73); + $this->r->add('map', $this->p->readString()); + $this->p->jumpto(105); + $this->r->add('motd', $this->p->readString()); + $this->p->jumpto(206); + $this->r->add('rate', $this->p->readInt8()); + $this->r->add('numplayers', $this->p->readInt8()); + $this->r->add('maxplayers', $this->p->readInt8()); + $this->r->add('numbots', $this->p->readInt8()); + $this->r->add('session', $this->p->readInt8() >> 5); + $this->r->add('damage', $this->p->readInt8()); + $this->p->jumpto(217); + $this->r->add('time', $this->p->readInt16()); + $this->r->add('laps', $this->p->readInt16() / 16); + } +} +?> diff --git a/reference/rfactor/lgsl.txt b/reference/rfactor/lgsl.txt index 8c340a9..927c168 100644 --- a/reference/rfactor/lgsl.txt +++ b/reference/rfactor/lgsl.txt @@ -1,94 +1,94 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - function lgsl_query_16(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: -// http://www.planetpointy.co.uk/software/rfactorsspy.shtml -// http://users.pandora.be/viperius/mUtil/ -// USES FIXED DATA POSITIONS WITH RANDOM CHARACTERS FILLING THE GAPS - - fwrite($lgsl_fp, "rF_S"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - -// $server['e']['gamename'] = lgsl_get_string($buffer); - $buffer = substr($buffer, 8); -// $server['e']['fullupdate'] = lgsl_unpack($buffer[0], "C"); - $server['e']['region'] = lgsl_unpack($buffer[1] .$buffer[2], "S"); -// $server['e']['ip'] = ($buffer[3] .$buffer[4].$buffer[5].$buffer[6]); // UNSIGNED LONG -// $server['e']['size'] = lgsl_unpack($buffer[7] .$buffer[8], "S"); - $server['e']['version'] = lgsl_unpack($buffer[9] .$buffer[10], "S"); -// $server['e']['version_racecast'] = lgsl_unpack($buffer[11].$buffer[12], "S"); - $server['e']['hostport'] = lgsl_unpack($buffer[13].$buffer[14], "S"); -// $server['e']['queryport'] = lgsl_unpack($buffer[15].$buffer[16], "S"); - $buffer = substr($buffer, 17); - $server['s']['game'] = lgsl_get_string($buffer); - $buffer = substr($buffer, 20); - $server['s']['name'] = lgsl_get_string($buffer); - $buffer = substr($buffer, 28); - $server['s']['map'] = lgsl_get_string($buffer); - $buffer = substr($buffer, 32); - $server['e']['motd'] = lgsl_get_string($buffer); - $buffer = substr($buffer, 96); - $server['e']['packed_aids'] = lgsl_unpack($buffer[0].$buffer[1], "S"); -// $server['e']['ping'] = lgsl_unpack($buffer[2].$buffer[3], "S"); - $server['e']['packed_flags'] = lgsl_unpack($buffer[4], "C"); - $server['e']['rate'] = lgsl_unpack($buffer[5], "C"); - $server['s']['players'] = lgsl_unpack($buffer[6], "C"); - $server['s']['playersmax'] = lgsl_unpack($buffer[7], "C"); - $server['e']['bots'] = lgsl_unpack($buffer[8], "C"); - $server['e']['packed_special'] = lgsl_unpack($buffer[9], "C"); - $server['e']['damage'] = lgsl_unpack($buffer[10], "C"); - $server['e']['packed_rules'] = lgsl_unpack($buffer[11].$buffer[12], "S"); - $server['e']['credits1'] = lgsl_unpack($buffer[13], "C"); - $server['e']['credits2'] = lgsl_unpack($buffer[14].$buffer[15], "S"); - $server['e']['time'] = lgsl_time(lgsl_unpack($buffer[16].$buffer[17], "S")); - $server['e']['laps'] = lgsl_unpack($buffer[18].$buffer[19], "s") / 16; - $buffer = substr($buffer, 23); - $server['e']['vehicles'] = lgsl_get_string($buffer); - - // DOES NOT RETURN PLAYER INFORMATION - -//---------------------------------------------------------+ - - $server['s']['password'] = ($server['e']['packed_special'] & 2) ? 1 : 0; - $server['e']['racecast'] = ($server['e']['packed_special'] & 4) ? 1 : 0; - $server['e']['fixedsetups'] = ($server['e']['packed_special'] & 16) ? 1 : 0; - - $server['e']['aids'] = ""; - if ($server['e']['packed_aids'] & 1) { $server['e']['aids'] .= " TractionControl"; } - if ($server['e']['packed_aids'] & 2) { $server['e']['aids'] .= " AntiLockBraking"; } - if ($server['e']['packed_aids'] & 4) { $server['e']['aids'] .= " StabilityControl"; } - if ($server['e']['packed_aids'] & 8) { $server['e']['aids'] .= " AutoShifting"; } - if ($server['e']['packed_aids'] & 16) { $server['e']['aids'] .= " AutoClutch"; } - if ($server['e']['packed_aids'] & 32) { $server['e']['aids'] .= " Invulnerability"; } - if ($server['e']['packed_aids'] & 64) { $server['e']['aids'] .= " OppositeLock"; } - if ($server['e']['packed_aids'] & 128) { $server['e']['aids'] .= " SteeringHelp"; } - if ($server['e']['packed_aids'] & 256) { $server['e']['aids'] .= " BrakingHelp"; } - if ($server['e']['packed_aids'] & 512) { $server['e']['aids'] .= " SpinRecovery"; } - if ($server['e']['packed_aids'] & 1024) { $server['e']['aids'] .= " AutoPitstop"; } - - $server['e']['aids'] = str_replace(" ", " / ", trim($server['e']['aids'])); - $server['e']['vehicles'] = str_replace("|", " / ", trim($server['e']['vehicles'])); - - unset($server['e']['packed_aids']); - unset($server['e']['packed_flags']); - unset($server['e']['packed_special']); - unset($server['e']['packed_rules']); - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + function lgsl_query_16(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: +// http://www.planetpointy.co.uk/software/rfactorsspy.shtml +// http://users.pandora.be/viperius/mUtil/ +// USES FIXED DATA POSITIONS WITH RANDOM CHARACTERS FILLING THE GAPS + + fwrite($lgsl_fp, "rF_S"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + +// $server['e']['gamename'] = lgsl_get_string($buffer); + $buffer = substr($buffer, 8); +// $server['e']['fullupdate'] = lgsl_unpack($buffer[0], "C"); + $server['e']['region'] = lgsl_unpack($buffer[1] .$buffer[2], "S"); +// $server['e']['ip'] = ($buffer[3] .$buffer[4].$buffer[5].$buffer[6]); // UNSIGNED LONG +// $server['e']['size'] = lgsl_unpack($buffer[7] .$buffer[8], "S"); + $server['e']['version'] = lgsl_unpack($buffer[9] .$buffer[10], "S"); +// $server['e']['version_racecast'] = lgsl_unpack($buffer[11].$buffer[12], "S"); + $server['e']['hostport'] = lgsl_unpack($buffer[13].$buffer[14], "S"); +// $server['e']['queryport'] = lgsl_unpack($buffer[15].$buffer[16], "S"); + $buffer = substr($buffer, 17); + $server['s']['game'] = lgsl_get_string($buffer); + $buffer = substr($buffer, 20); + $server['s']['name'] = lgsl_get_string($buffer); + $buffer = substr($buffer, 28); + $server['s']['map'] = lgsl_get_string($buffer); + $buffer = substr($buffer, 32); + $server['e']['motd'] = lgsl_get_string($buffer); + $buffer = substr($buffer, 96); + $server['e']['packed_aids'] = lgsl_unpack($buffer[0].$buffer[1], "S"); +// $server['e']['ping'] = lgsl_unpack($buffer[2].$buffer[3], "S"); + $server['e']['packed_flags'] = lgsl_unpack($buffer[4], "C"); + $server['e']['rate'] = lgsl_unpack($buffer[5], "C"); + $server['s']['players'] = lgsl_unpack($buffer[6], "C"); + $server['s']['playersmax'] = lgsl_unpack($buffer[7], "C"); + $server['e']['bots'] = lgsl_unpack($buffer[8], "C"); + $server['e']['packed_special'] = lgsl_unpack($buffer[9], "C"); + $server['e']['damage'] = lgsl_unpack($buffer[10], "C"); + $server['e']['packed_rules'] = lgsl_unpack($buffer[11].$buffer[12], "S"); + $server['e']['credits1'] = lgsl_unpack($buffer[13], "C"); + $server['e']['credits2'] = lgsl_unpack($buffer[14].$buffer[15], "S"); + $server['e']['time'] = lgsl_time(lgsl_unpack($buffer[16].$buffer[17], "S")); + $server['e']['laps'] = lgsl_unpack($buffer[18].$buffer[19], "s") / 16; + $buffer = substr($buffer, 23); + $server['e']['vehicles'] = lgsl_get_string($buffer); + + // DOES NOT RETURN PLAYER INFORMATION + +//---------------------------------------------------------+ + + $server['s']['password'] = ($server['e']['packed_special'] & 2) ? 1 : 0; + $server['e']['racecast'] = ($server['e']['packed_special'] & 4) ? 1 : 0; + $server['e']['fixedsetups'] = ($server['e']['packed_special'] & 16) ? 1 : 0; + + $server['e']['aids'] = ""; + if ($server['e']['packed_aids'] & 1) { $server['e']['aids'] .= " TractionControl"; } + if ($server['e']['packed_aids'] & 2) { $server['e']['aids'] .= " AntiLockBraking"; } + if ($server['e']['packed_aids'] & 4) { $server['e']['aids'] .= " StabilityControl"; } + if ($server['e']['packed_aids'] & 8) { $server['e']['aids'] .= " AutoShifting"; } + if ($server['e']['packed_aids'] & 16) { $server['e']['aids'] .= " AutoClutch"; } + if ($server['e']['packed_aids'] & 32) { $server['e']['aids'] .= " Invulnerability"; } + if ($server['e']['packed_aids'] & 64) { $server['e']['aids'] .= " OppositeLock"; } + if ($server['e']['packed_aids'] & 128) { $server['e']['aids'] .= " SteeringHelp"; } + if ($server['e']['packed_aids'] & 256) { $server['e']['aids'] .= " BrakingHelp"; } + if ($server['e']['packed_aids'] & 512) { $server['e']['aids'] .= " SpinRecovery"; } + if ($server['e']['packed_aids'] & 1024) { $server['e']['aids'] .= " AutoPitstop"; } + + $server['e']['aids'] = str_replace(" ", " / ", trim($server['e']['aids'])); + $server['e']['vehicles'] = str_replace("|", " / ", trim($server['e']['vehicles'])); + + unset($server['e']['packed_aids']); + unset($server['e']['packed_flags']); + unset($server['e']['packed_special']); + unset($server['e']['packed_rules']); + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/samp/lgsl.txt b/reference/samp/lgsl.txt index 2ea1a2e..899cf14 100644 --- a/reference/samp/lgsl.txt +++ b/reference/samp/lgsl.txt @@ -1,92 +1,92 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - - function lgsl_query_12(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: -// VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE - - if ($server['b']['type'] == "samp") { $challenge_packet = "SAMP\x21\x21\x21\x21\x00\x00"; } - elseif ($server['b']['type'] == "vcmp") { $challenge_packet = "VCMP\x21\x21\x21\x21\x00\x00"; $lgsl_need['e'] = FALSE; $lgsl_need['p'] = FALSE; } - - if ($lgsl_need['s']) { $challenge_packet .= "i"; } - elseif ($lgsl_need['e']) { $challenge_packet .= "r"; } - elseif ($lgsl_need['p']) { $challenge_packet .= "d"; } - - fwrite($lgsl_fp, $challenge_packet); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 10); // REMOVE HEADER - - $response_type = lgsl_cut_byte($buffer, 1); - -//---------------------------------------------------------+ - - if ($response_type == "i") - { - $lgsl_need['s'] = FALSE; - - $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['s']['name'] = lgsl_cut_pascal($buffer, 4); - $server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4); - $server['s']['map'] = lgsl_cut_pascal($buffer, 4); - } - -//---------------------------------------------------------+ - - elseif ($response_type == "r") - { - $lgsl_need['e'] = FALSE; - - $item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - - for ($i=0; $i<$item_total; $i++) - { - if (!$buffer) { return FALSE; } - - $data_key = strtolower(lgsl_cut_pascal($buffer)); - $data_value = lgsl_cut_pascal($buffer); - - $server['e'][$data_key] = $data_value; - } - } - -//---------------------------------------------------------+ - - elseif ($response_type == "d") - { - $lgsl_need['p'] = FALSE; - - $player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - - for ($i=0; $i<$player_total; $i++) - { - if (!$buffer) { return FALSE; } - - $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); - $server['p'][$i]['name'] = lgsl_cut_pascal($buffer); - $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); - $server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); - } - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + + function lgsl_query_12(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: +// VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE + + if ($server['b']['type'] == "samp") { $challenge_packet = "SAMP\x21\x21\x21\x21\x00\x00"; } + elseif ($server['b']['type'] == "vcmp") { $challenge_packet = "VCMP\x21\x21\x21\x21\x00\x00"; $lgsl_need['e'] = FALSE; $lgsl_need['p'] = FALSE; } + + if ($lgsl_need['s']) { $challenge_packet .= "i"; } + elseif ($lgsl_need['e']) { $challenge_packet .= "r"; } + elseif ($lgsl_need['p']) { $challenge_packet .= "d"; } + + fwrite($lgsl_fp, $challenge_packet); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 10); // REMOVE HEADER + + $response_type = lgsl_cut_byte($buffer, 1); + +//---------------------------------------------------------+ + + if ($response_type == "i") + { + $lgsl_need['s'] = FALSE; + + $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['s']['name'] = lgsl_cut_pascal($buffer, 4); + $server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4); + $server['s']['map'] = lgsl_cut_pascal($buffer, 4); + } + +//---------------------------------------------------------+ + + elseif ($response_type == "r") + { + $lgsl_need['e'] = FALSE; + + $item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + + for ($i=0; $i<$item_total; $i++) + { + if (!$buffer) { return FALSE; } + + $data_key = strtolower(lgsl_cut_pascal($buffer)); + $data_value = lgsl_cut_pascal($buffer); + + $server['e'][$data_key] = $data_value; + } + } + +//---------------------------------------------------------+ + + elseif ($response_type == "d") + { + $lgsl_need['p'] = FALSE; + + $player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + + for ($i=0; $i<$player_total; $i++) + { + if (!$buffer) { return FALSE; } + + $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); + $server['p'][$i]['name'] = lgsl_cut_pascal($buffer); + $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); + $server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); + } + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/savage/gameq.txt b/reference/savage/gameq.txt index 5ac3a06..a3faaa7 100644 --- a/reference/savage/gameq.txt +++ b/reference/savage/gameq.txt @@ -1,92 +1,92 @@ -. - * - * $Id: silverback.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $ - */ - -[silverback] -status = "\x9e\x4c\x23\x00\x00\xcePiNG" - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Silverback Engine Protocol - * (Savage) - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_silverback extends GameQ_Protocol -{ - /* - * status packet - */ - public function status() - { - while ($this->p->getLength()) { - $var = $this->p->readString("\xFE"); - - if ($var == 'players') break; - - $this->r->add($var, $this->p->readString("\xFF")); - } - - $this->players(); - } - - /* - * player / team data - */ - public function players() - { - $team = ''; - $players = 0; - - while ($this->p->getLength()) { - if ($this->p->lookAhead() == "\x20") { - $this->p->skip(); - $this->r->addPlayer('name', $this->p->readString("\x0a")); - $this->r->addPlayer('team', $team); - ++$players; - } - else { - $team = $this->p->readString("\x0a"); - if ($team != '--empty--') { - $this->r->addTeam('name', $team); - } - } - } - } - - - /* - * Merge packets - */ - public function preprocess($packets) - { - // Cut off headers and join packets - $return = ''; - - foreach ($packets as $packet) { - $return .= substr($packet, 12, strlen($packet) - 13); - } - - return $return; - } -} -?> +. + * + * $Id: silverback.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $ + */ + +[silverback] +status = "\x9e\x4c\x23\x00\x00\xcePiNG" + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Silverback Engine Protocol + * (Savage) + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_silverback extends GameQ_Protocol +{ + /* + * status packet + */ + public function status() + { + while ($this->p->getLength()) { + $var = $this->p->readString("\xFE"); + + if ($var == 'players') break; + + $this->r->add($var, $this->p->readString("\xFF")); + } + + $this->players(); + } + + /* + * player / team data + */ + public function players() + { + $team = ''; + $players = 0; + + while ($this->p->getLength()) { + if ($this->p->lookAhead() == "\x20") { + $this->p->skip(); + $this->r->addPlayer('name', $this->p->readString("\x0a")); + $this->r->addPlayer('team', $team); + ++$players; + } + else { + $team = $this->p->readString("\x0a"); + if ($team != '--empty--') { + $this->r->addTeam('name', $team); + } + } + } + } + + + /* + * Merge packets + */ + public function preprocess($packets) + { + // Cut off headers and join packets + $return = ''; + + foreach ($packets as $packet) { + $return .= substr($packet, 12, strlen($packet) - 13); + } + + return $return; + } +} +?> diff --git a/reference/savage/lgsl.txt b/reference/savage/lgsl.txt index 3d4d264..8c2d738 100644 --- a/reference/savage/lgsl.txt +++ b/reference/savage/lgsl.txt @@ -1,66 +1,66 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_17(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: http://masterserver.savage.s2games.com - - fwrite($lgsl_fp, "\x9e\x4c\x23\x00\x00\xce\x21\x21\x21\x21"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 12); // REMOVE HEADER - - while ($key = strtolower(lgsl_cut_string($buffer, 0, "\xFE"))) - { - if ($key == "players") { break; } - - $value = lgsl_cut_string($buffer, 0, "\xFF"); - $value = str_replace("\x00", "", $value); - $value = lgsl_parse_color($value, $server['b']['type']); - - $server['e'][$key] = $value; - } - - $server['s']['name'] = $server['e']['name']; unset($server['e']['name']); - $server['s']['map'] = $server['e']['world']; unset($server['e']['world']); - $server['s']['players'] = $server['e']['cnum']; unset($server['e']['cnum']); - $server['s']['playersmax'] = $server['e']['cmax']; unset($server['e']['cnum']); - $server['s']['password'] = $server['e']['pass']; unset($server['e']['cnum']); - -//---------------------------------------------------------+ - - $server['t'][0]['name'] = $server['e']['race1']; - $server['t'][1]['name'] = $server['e']['race2']; - $server['t'][2]['name'] = "spectator"; - - $team_key = -1; - $player_key = 0; - - while ($value = lgsl_cut_string($buffer, 0, "\x0a")) - { - if ($value[0] == "\x00") { break; } - if ($value[0] != "\x20") { $team_key++; continue; } - - $server['p'][$player_key]['name'] = lgsl_parse_color(substr($value, 1), $server['b']['type']); - $server['p'][$player_key]['team'] = $server['t'][$team_key]['name']; - - $player_key++; - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + function lgsl_query_17(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: http://masterserver.savage.s2games.com + + fwrite($lgsl_fp, "\x9e\x4c\x23\x00\x00\xce\x21\x21\x21\x21"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 12); // REMOVE HEADER + + while ($key = strtolower(lgsl_cut_string($buffer, 0, "\xFE"))) + { + if ($key == "players") { break; } + + $value = lgsl_cut_string($buffer, 0, "\xFF"); + $value = str_replace("\x00", "", $value); + $value = lgsl_parse_color($value, $server['b']['type']); + + $server['e'][$key] = $value; + } + + $server['s']['name'] = $server['e']['name']; unset($server['e']['name']); + $server['s']['map'] = $server['e']['world']; unset($server['e']['world']); + $server['s']['players'] = $server['e']['cnum']; unset($server['e']['cnum']); + $server['s']['playersmax'] = $server['e']['cmax']; unset($server['e']['cnum']); + $server['s']['password'] = $server['e']['pass']; unset($server['e']['cnum']); + +//---------------------------------------------------------+ + + $server['t'][0]['name'] = $server['e']['race1']; + $server['t'][1]['name'] = $server['e']['race2']; + $server['t'][2]['name'] = "spectator"; + + $team_key = -1; + $player_key = 0; + + while ($value = lgsl_cut_string($buffer, 0, "\x0a")) + { + if ($value[0] == "\x00") { break; } + if ($value[0] != "\x20") { $team_key++; continue; } + + $server['p'][$player_key]['name'] = lgsl_parse_color(substr($value, 1), $server['b']['type']); + $server['p'][$player_key]['team'] = $server['t'][$team_key]['name']; + + $player_key++; + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/savage/qstat.txt b/reference/savage/qstat.txt index b3bce33..249bd9a 100644 --- a/reference/savage/qstat.txt +++ b/reference/savage/qstat.txt @@ -1,278 +1,278 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - -unsigned char savage_serverquery[] = { - 0x9e,0x4c,0x23,0x00,0x00,0xc8,0x01,0x21,0x00,0x00 -}; - -unsigned char savage_playerquery[] = { - 0x9e,0x4c,0x23,0x00,0x00,0xce,0x76,0x46,0x00,0x00 -}; - - -{ - /* SAVAGE PROTOCOL */ - SAVAGE_SERVER, /* id */ - "SAS", /* type_prefix */ - "sas", /* type_string */ - "-sas", /* type_option */ - "Savage", /* game_name */ - 0, /* master */ - SAVAGE_DEFAULT_PORT, /* default_port */ - 0, /* port_offset */ - TF_QUERY_ARG, /* flags */ - "gametype", /* game_rule */ - "SAVAGE", /* template_var */ - (char*)savage_serverquery, /* status_packet */ - sizeof( savage_serverquery ) - 1, /* status_len */ - (char*)savage_playerquery, /* player_packet */ - sizeof( savage_playerquery ) - 1, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_savage_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_savage_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_savage_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_savage_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_savage_packet, /* packet_func */ -}, - - -query_status_t send_savage_request_packet(struct qserver *server) -{ - int len; - char *pkt; - - if (get_player_info) - { - pkt = server->type->player_packet; - len = server->type->player_len; - } - else - { - pkt = server->type->status_packet; - len = server->type->status_len; - } - - return send_packet(server, pkt, len); -} - - - -query_status_t deal_with_savage_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - char *s, *key, *value, *end; - - debug( 2, "deal_with_savage_packet %p, %d", server, pktlen ); - - server->n_servers++; - if (NULL == server->server_name) - { - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - } - else - { - gettimeofday(&server->packet_time1, NULL); - } - - rawpkt[pktlen] = '\0'; - - end = s = rawpkt; - end += pktlen; - while (*s) - { - // Find the seperator - while (s <= end && *s != '\xFF') - { - s++; - } - - if (s >= end) - { - // Hit the end no more - break; - } - - // key start - key = ++s; - while (s < end && *s != '\xFE') - { - s++; - } - if (*s != '\xFE') - { - // malformed - break; - } - *s++ = '\0'; - // key end - // value start - value = s; - - while (s < end && *s != '\xFF') - { - s++; - } - - if (*s == '\xFF') - { - *s = '\0'; - } - //fprintf( stderr, "'%s' = '%s'\n", key, value ); - - // Decode current key par - if (0 == strcmp("cmax", key)) - { - // Max players - server->max_players = atoi(value); - } - else if (0 == strcmp("cnum", key)) - { - // Current players - server->num_players = atoi(value); - } - else if (0 == strcmp("bal", key)) - { - // Balance - add_rule(server, "Balance", value, NO_FLAGS); - } - else if (0 == strcmp("world", key)) - { - // Current map - server->map_name = strdup(value); - } - else if (0 == strcmp("gametype", key)) - { - // Game type - server->game = find_savage_game(value); - add_rule(server, server->type->game_rule, server->game, NO_FLAGS); - } - else if (0 == strcmp("pure", key)) - { - // Pure - add_rule(server, "Pure", value, NO_FLAGS); - } - else if (0 == strcmp("time", key)) - { - // Current game time - add_rule(server, "Time", value, NO_FLAGS); - } - else if (0 == strcmp("notes", key)) - { - // Notes - add_rule(server, "Notes", value, NO_FLAGS); - } - else if (0 == strcmp("needcmdr", key)) - { - // Need Commander - add_rule(server, "Need Commander", value, NO_FLAGS); - } - else if (0 == strcmp("name", key)) - { - // Server name - server->server_name = strdup(value); - } - else if (0 == strcmp("fw", key)) - { - // Firewalled - add_rule(server, "Firewalled", value, NO_FLAGS); - } - else if (0 == strcmp("players", key)) - { - - // Players names - int player_number = 0; - int team_number = 1; - char *team_name, *player_name, *n; - n = team_name = value; - - // team name - n++; - while (*n && *n != '\x0a') - { - n++; - } - - if (*n != '\x0a') - { - // Broken data - break; - } - *n = '\0'; - - player_name = ++n; - while (*n) - { - while (*n && *n != '\x0a') - { - n++; - } - - if (*n != '\x0a') - { - // Broken data - break; - } - *n = '\0'; - n++; - - if (0 == strncmp("Team ", player_name, 5)) - { - team_name = player_name; - team_number++; - } - else - { - if (0 != strlen(player_name)) - { - struct player *player = add_player(server, player_number); - if (NULL != player) - { - player->name = strdup(player_name); - player->team = team_number; - player->team_name = strdup(team_name); - } player_number++; - } - } - player_name = n; - } - } - - *s = '\xFF'; - } - - return DONE_FORCE; -} - - - -char *find_savage_game(char *gametype) -{ - if (0 == strcmp("RTSS", gametype)) - { - return strdup("RTSS"); - } - else - { - return strdup("Unknown"); - } -} - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + +unsigned char savage_serverquery[] = { + 0x9e,0x4c,0x23,0x00,0x00,0xc8,0x01,0x21,0x00,0x00 +}; + +unsigned char savage_playerquery[] = { + 0x9e,0x4c,0x23,0x00,0x00,0xce,0x76,0x46,0x00,0x00 +}; + + +{ + /* SAVAGE PROTOCOL */ + SAVAGE_SERVER, /* id */ + "SAS", /* type_prefix */ + "sas", /* type_string */ + "-sas", /* type_option */ + "Savage", /* game_name */ + 0, /* master */ + SAVAGE_DEFAULT_PORT, /* default_port */ + 0, /* port_offset */ + TF_QUERY_ARG, /* flags */ + "gametype", /* game_rule */ + "SAVAGE", /* template_var */ + (char*)savage_serverquery, /* status_packet */ + sizeof( savage_serverquery ) - 1, /* status_len */ + (char*)savage_playerquery, /* player_packet */ + sizeof( savage_playerquery ) - 1, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_savage_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_savage_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_savage_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_savage_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_savage_packet, /* packet_func */ +}, + + +query_status_t send_savage_request_packet(struct qserver *server) +{ + int len; + char *pkt; + + if (get_player_info) + { + pkt = server->type->player_packet; + len = server->type->player_len; + } + else + { + pkt = server->type->status_packet; + len = server->type->status_len; + } + + return send_packet(server, pkt, len); +} + + + +query_status_t deal_with_savage_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + char *s, *key, *value, *end; + + debug( 2, "deal_with_savage_packet %p, %d", server, pktlen ); + + server->n_servers++; + if (NULL == server->server_name) + { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } + else + { + gettimeofday(&server->packet_time1, NULL); + } + + rawpkt[pktlen] = '\0'; + + end = s = rawpkt; + end += pktlen; + while (*s) + { + // Find the seperator + while (s <= end && *s != '\xFF') + { + s++; + } + + if (s >= end) + { + // Hit the end no more + break; + } + + // key start + key = ++s; + while (s < end && *s != '\xFE') + { + s++; + } + if (*s != '\xFE') + { + // malformed + break; + } + *s++ = '\0'; + // key end + // value start + value = s; + + while (s < end && *s != '\xFF') + { + s++; + } + + if (*s == '\xFF') + { + *s = '\0'; + } + //fprintf( stderr, "'%s' = '%s'\n", key, value ); + + // Decode current key par + if (0 == strcmp("cmax", key)) + { + // Max players + server->max_players = atoi(value); + } + else if (0 == strcmp("cnum", key)) + { + // Current players + server->num_players = atoi(value); + } + else if (0 == strcmp("bal", key)) + { + // Balance + add_rule(server, "Balance", value, NO_FLAGS); + } + else if (0 == strcmp("world", key)) + { + // Current map + server->map_name = strdup(value); + } + else if (0 == strcmp("gametype", key)) + { + // Game type + server->game = find_savage_game(value); + add_rule(server, server->type->game_rule, server->game, NO_FLAGS); + } + else if (0 == strcmp("pure", key)) + { + // Pure + add_rule(server, "Pure", value, NO_FLAGS); + } + else if (0 == strcmp("time", key)) + { + // Current game time + add_rule(server, "Time", value, NO_FLAGS); + } + else if (0 == strcmp("notes", key)) + { + // Notes + add_rule(server, "Notes", value, NO_FLAGS); + } + else if (0 == strcmp("needcmdr", key)) + { + // Need Commander + add_rule(server, "Need Commander", value, NO_FLAGS); + } + else if (0 == strcmp("name", key)) + { + // Server name + server->server_name = strdup(value); + } + else if (0 == strcmp("fw", key)) + { + // Firewalled + add_rule(server, "Firewalled", value, NO_FLAGS); + } + else if (0 == strcmp("players", key)) + { + + // Players names + int player_number = 0; + int team_number = 1; + char *team_name, *player_name, *n; + n = team_name = value; + + // team name + n++; + while (*n && *n != '\x0a') + { + n++; + } + + if (*n != '\x0a') + { + // Broken data + break; + } + *n = '\0'; + + player_name = ++n; + while (*n) + { + while (*n && *n != '\x0a') + { + n++; + } + + if (*n != '\x0a') + { + // Broken data + break; + } + *n = '\0'; + n++; + + if (0 == strncmp("Team ", player_name, 5)) + { + team_name = player_name; + team_number++; + } + else + { + if (0 != strlen(player_name)) + { + struct player *player = add_player(server, player_number); + if (NULL != player) + { + player->name = strdup(player_name); + player->team = team_number; + player->team_name = strdup(team_name); + } player_number++; + } + } + player_name = n; + } + } + + *s = '\xFF'; + } + + return DONE_FORCE; +} + + + +char *find_savage_game(char *gametype) +{ + if (0 == strcmp("RTSS", gametype)) + { + return strdup("RTSS"); + } + else + { + return strdup("Unknown"); + } +} + diff --git a/reference/savage2/gameq.txt b/reference/savage2/gameq.txt index 866fb6c..3d6b2d2 100644 --- a/reference/savage2/gameq.txt +++ b/reference/savage2/gameq.txt @@ -1,55 +1,55 @@ -. - * - * $Id: savage2.php,v 1.1 2008/05/22 14:16:11 tombuskens Exp $ - */ - -[savage2] -status = "\x01" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Savage 2 Protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_savage2 extends GameQ_Protocol -{ - /* - * status packet - */ - public function status() - { - $this->p->skip(12); - $this->r->add('hostname', $this->p->readString()); - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('max_players', $this->p->readInt8()); - $this->r->add('time', $this->p->readString()); - $this->r->add('map', $this->p->readString()); - $this->r->add('nextmap', $this->p->readString()); - $this->r->add('location', $this->p->readString()); - $this->r->add('min_players', $this->p->readInt8()); - $this->r->add('gametype', $this->p->readString()); - $this->r->add('version', $this->p->readString()); - $this->r->add('min_level', $this->p->readInt8()); - } -} -?> +. + * + * $Id: savage2.php,v 1.1 2008/05/22 14:16:11 tombuskens Exp $ + */ + +[savage2] +status = "\x01" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Savage 2 Protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_savage2 extends GameQ_Protocol +{ + /* + * status packet + */ + public function status() + { + $this->p->skip(12); + $this->r->add('hostname', $this->p->readString()); + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('max_players', $this->p->readInt8()); + $this->r->add('time', $this->p->readString()); + $this->r->add('map', $this->p->readString()); + $this->r->add('nextmap', $this->p->readString()); + $this->r->add('location', $this->p->readString()); + $this->r->add('min_players', $this->p->readInt8()); + $this->r->add('gametype', $this->p->readString()); + $this->r->add('version', $this->p->readString()); + $this->r->add('min_level', $this->p->readInt8()); + } +} +?> diff --git a/reference/savage2/lgsl.txt b/reference/savage2/lgsl.txt index 0198362..d4149e3 100644 --- a/reference/savage2/lgsl.txt +++ b/reference/savage2/lgsl.txt @@ -1,46 +1,46 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - -//------------------------------------------------------------------------------------------------------------+ -//------------------------------------------------------------------------------------------------------------+ - - function lgsl_query_18(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: http://masterserver.savage2.s2games.com - - fwrite($lgsl_fp, "\x01"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 12); // REMOVE HEADER - - $server['s']['name'] = lgsl_cut_string($buffer); - $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['time'] = lgsl_cut_string($buffer); - $server['s']['map'] = lgsl_cut_string($buffer); - $server['e']['nextmap'] = lgsl_cut_string($buffer); - $server['e']['location'] = lgsl_cut_string($buffer); - $server['e']['minimum_players'] = ord(lgsl_cut_string($buffer)); - $server['e']['gamemode'] = lgsl_cut_string($buffer); - $server['e']['version'] = lgsl_cut_string($buffer); - $server['e']['minimum_level'] = ord(lgsl_cut_byte($buffer, 1)); - - // DOES NOT RETURN PLAYER INFORMATION - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + +//------------------------------------------------------------------------------------------------------------+ +//------------------------------------------------------------------------------------------------------------+ + + function lgsl_query_18(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: http://masterserver.savage2.s2games.com + + fwrite($lgsl_fp, "\x01"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 12); // REMOVE HEADER + + $server['s']['name'] = lgsl_cut_string($buffer); + $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['time'] = lgsl_cut_string($buffer); + $server['s']['map'] = lgsl_cut_string($buffer); + $server['e']['nextmap'] = lgsl_cut_string($buffer); + $server['e']['location'] = lgsl_cut_string($buffer); + $server['e']['minimum_players'] = ord(lgsl_cut_string($buffer)); + $server['e']['gamemode'] = lgsl_cut_string($buffer); + $server['e']['version'] = lgsl_cut_string($buffer); + $server['e']['minimum_level'] = ord(lgsl_cut_byte($buffer, 1)); + + // DOES NOT RETURN PLAYER INFORMATION + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/teeworlds/gameq.txt b/reference/teeworlds/gameq.txt index 7355adc..673c710 100644 --- a/reference/teeworlds/gameq.txt +++ b/reference/teeworlds/gameq.txt @@ -1,65 +1,65 @@ -. - * - * $Id: teeworlds.php,v 1.2 2009/03/09 13:36:32 tombuskens Exp $ - */ - -[teeworlds] -status = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Teeworlds protocol - * - * @author Tom Buskens - * @version $Revision: 1.2 $ - */ -class GameQ_Protocol_teeworlds extends GameQ_Protocol -{ - /* - * status packet - */ - public function status() - { - $this->p->skip(14); - - $this->r->add('version', $this->p->readString()); - $this->r->add('hostname', $this->p->readString()); - $this->r->add('map', $this->p->readString()); - $this->r->add('gametype', $this->p->readString()); - - - $this->r->add('password', $this->p->readString()); - $this->r->add('ping', $this->p->readString()); - $this->r->add('num_players', $this->p->readString()); - $this->r->add('max_players', $this->p->readString()); - - $this->players(); - } - - private function players() - { - while ($name = $this->p->readString()) { - $this->r->addPlayer('name', $name); - $this->r->addPlayer('score', $this->p->readString()); - } - } -} -?> +. + * + * $Id: teeworlds.php,v 1.2 2009/03/09 13:36:32 tombuskens Exp $ + */ + +[teeworlds] +status = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Teeworlds protocol + * + * @author Tom Buskens + * @version $Revision: 1.2 $ + */ +class GameQ_Protocol_teeworlds extends GameQ_Protocol +{ + /* + * status packet + */ + public function status() + { + $this->p->skip(14); + + $this->r->add('version', $this->p->readString()); + $this->r->add('hostname', $this->p->readString()); + $this->r->add('map', $this->p->readString()); + $this->r->add('gametype', $this->p->readString()); + + + $this->r->add('password', $this->p->readString()); + $this->r->add('ping', $this->p->readString()); + $this->r->add('num_players', $this->p->readString()); + $this->r->add('max_players', $this->p->readString()); + + $this->players(); + } + + private function players() + { + while ($name = $this->p->readString()) { + $this->r->addPlayer('name', $name); + $this->r->addPlayer('score', $this->p->readString()); + } + } +} +?> diff --git a/reference/teeworlds/gameq2.txt b/reference/teeworlds/gameq2.txt index 4368455..41b6b4d 100644 --- a/reference/teeworlds/gameq2.txt +++ b/reference/teeworlds/gameq2.txt @@ -1,109 +1,109 @@ -. - */ - -/** - * Teeworlds Protocol - * - * @author Marcel Bößendörfer - */ -class GameQ_Protocols_Teeworlds extends GameQ_Protocols { - - /** - * Array of packets we want to look up. - * Each key should correspond to a defined method in this or a parent class - * - * @var array - */ - protected $packets = array( - self::PACKET_ALL => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x67\x69\x65\x33\x05", - // 0.5 Packet (not compatible, maybe some wants to implement "Teeworldsold") - //self::PACKET_STATUS => "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief", - ); - - /** - * Methods to be run when processing the response(s) - * - * @var array - */ - protected $process_methods = array( - "process_all" - ); - - /** - * Default port for this server type - * - * @var int - */ - protected $port = 8303; // Default port, used if not set when instanced - - /** - * The protocol being used - * - * @var string - */ - protected $protocol = 'teeworlds'; - - /** - * String name of this protocol class - * - * @var string - */ - protected $name = 'teeworlds'; - - /** - * Longer string name of this protocol class - * - * @var string - */ - protected $name_long = "Teeworlds"; - - /* - * Internal methods - */ - - protected function process_all() { - if(!$this->hasValidResponse(self::PACKET_ALL)) - { - return array(); - } - $data = $this->packets_response[self::PACKET_ALL][0]; - $buf = new GameQ_Buffer($data); - $result = new GameQ_Result(); - $buf->readString(); - $result->add('version', $buf->readString()); - $result->add('hostname', $buf->readString()); - $result->add('map', $buf->readString()); - $result->add('game_descr', $buf->readString()); - $result->add('flags', $buf->readString()); // not use about that - $result->add('num_players', $buf->readString()); - $result->add('maxplayers', $buf->readString()); - $result->add('num_players_total', $buf->readString()); - $result->add('maxplayers_total', $buf->readString()); - - // Players - while ($buf->getLength()) { - $result->addPlayer('name', $buf->readString()); - $result->addPlayer('clan', $buf->readString()); - $result->addPlayer('flag', $buf->readString()); - $result->addPlayer('score', $buf->readString()); - $result->addPlayer('team', $buf->readString()); - } - return $result->fetch(); - } +. + */ + +/** + * Teeworlds Protocol + * + * @author Marcel Bößendörfer + */ +class GameQ_Protocols_Teeworlds extends GameQ_Protocols { + + /** + * Array of packets we want to look up. + * Each key should correspond to a defined method in this or a parent class + * + * @var array + */ + protected $packets = array( + self::PACKET_ALL => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x67\x69\x65\x33\x05", + // 0.5 Packet (not compatible, maybe some wants to implement "Teeworldsold") + //self::PACKET_STATUS => "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief", + ); + + /** + * Methods to be run when processing the response(s) + * + * @var array + */ + protected $process_methods = array( + "process_all" + ); + + /** + * Default port for this server type + * + * @var int + */ + protected $port = 8303; // Default port, used if not set when instanced + + /** + * The protocol being used + * + * @var string + */ + protected $protocol = 'teeworlds'; + + /** + * String name of this protocol class + * + * @var string + */ + protected $name = 'teeworlds'; + + /** + * Longer string name of this protocol class + * + * @var string + */ + protected $name_long = "Teeworlds"; + + /* + * Internal methods + */ + + protected function process_all() { + if(!$this->hasValidResponse(self::PACKET_ALL)) + { + return array(); + } + $data = $this->packets_response[self::PACKET_ALL][0]; + $buf = new GameQ_Buffer($data); + $result = new GameQ_Result(); + $buf->readString(); + $result->add('version', $buf->readString()); + $result->add('hostname', $buf->readString()); + $result->add('map', $buf->readString()); + $result->add('game_descr', $buf->readString()); + $result->add('flags', $buf->readString()); // not use about that + $result->add('num_players', $buf->readString()); + $result->add('maxplayers', $buf->readString()); + $result->add('num_players_total', $buf->readString()); + $result->add('maxplayers_total', $buf->readString()); + + // Players + while ($buf->getLength()) { + $result->addPlayer('name', $buf->readString()); + $result->addPlayer('clan', $buf->readString()); + $result->addPlayer('flag', $buf->readString()); + $result->addPlayer('score', $buf->readString()); + $result->addPlayer('team', $buf->readString()); + } + return $result->fetch(); + } } \ No newline at end of file diff --git a/reference/teeworlds/lgsl.txt b/reference/teeworlds/lgsl.txt index f332936..7f25177 100644 --- a/reference/teeworlds/lgsl.txt +++ b/reference/teeworlds/lgsl.txt @@ -1,54 +1,54 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_21(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ - - fwrite($lgsl_fp,"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief"); - - $buffer = fread($lgsl_fp, 4096); - $buffer = substr($buffer, 20); // REMOVE HEADER - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $server['s']['name'] = lgsl_cut_string($buffer); - $server['s']['map'] = lgsl_cut_string($buffer); - $server['e']['gamemode'] = lgsl_cut_string($buffer); - $server['s']['password'] = lgsl_cut_string($buffer); - $server['e']['progress'] = lgsl_cut_string($buffer)."%"; - $server['s']['players'] = lgsl_cut_string($buffer); - $server['s']['playersmax'] = lgsl_cut_string($buffer); - - switch ($server['e']['gamemode']) - { - case 0: $server['e']['gamemode'] = "Deathmatch"; break; - case 1: $server['e']['gamemode'] = "Team Deathmatch"; break; - case 2: $server['e']['gamemode'] = "Capture The Flag"; break; - } - -//---------------------------------------------------------+ - - $player_key = 0; - - while ($buffer) - { - $server['p'][$player_key]['name'] = lgsl_cut_string($buffer); - $server['p'][$player_key]['score'] = lgsl_cut_string($buffer); - - $player_key ++; - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + function lgsl_query_21(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ + + fwrite($lgsl_fp,"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief"); + + $buffer = fread($lgsl_fp, 4096); + $buffer = substr($buffer, 20); // REMOVE HEADER + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $server['s']['name'] = lgsl_cut_string($buffer); + $server['s']['map'] = lgsl_cut_string($buffer); + $server['e']['gamemode'] = lgsl_cut_string($buffer); + $server['s']['password'] = lgsl_cut_string($buffer); + $server['e']['progress'] = lgsl_cut_string($buffer)."%"; + $server['s']['players'] = lgsl_cut_string($buffer); + $server['s']['playersmax'] = lgsl_cut_string($buffer); + + switch ($server['e']['gamemode']) + { + case 0: $server['e']['gamemode'] = "Deathmatch"; break; + case 1: $server['e']['gamemode'] = "Team Deathmatch"; break; + case 2: $server['e']['gamemode'] = "Capture The Flag"; break; + } + +//---------------------------------------------------------+ + + $player_key = 0; + + while ($buffer) + { + $server['p'][$player_key]['name'] = lgsl_cut_string($buffer); + $server['p'][$player_key]['score'] = lgsl_cut_string($buffer); + + $player_key ++; + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/teeworlds/qstat.txt b/reference/teeworlds/qstat.txt index d2a5b2d..b410a93 100644 --- a/reference/teeworlds/qstat.txt +++ b/reference/teeworlds/qstat.txt @@ -1,144 +1,144 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - -char tee_serverstatus[14] = { '\x20', '\0', '\0', '\0', '\0', '\0', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', 'f' }; - - - { - /* Teeworlds */ - TEE_SERVER, /* id */ - "TEE", /* type_prefix */ - "tee", /* type_string */ - "-tee", /* type_option */ - "Teeworlds", /* game_name */ - 0, /* master */ - 35515, /* default_port */ - 0, /* port_offset */ - 0, /* flags */ - "gametype", /* game_rule */ - "TEE", /* template_var */ - tee_serverstatus, /* status_packet */ - sizeof(tee_serverstatus), /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_tee_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_tee_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_tee_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_tee_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_tee_packet, /* packet_func */ -}, - - - -/* - * qstat 2.11 - * by Steve Jankowski - * - * Teeworlds protocol - * Copyright 2008 ? Emiliano Leporati - * - * Licensed under the Artistic License, see LICENSE.txt for license terms - * - */ - -#include -#include -#include - -#include "debug.h" -#include "qstat.h" -#include "packet_manip.h" - -char tee_serverinfo[8] = { '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o' }; - -query_status_t send_tee_request_packet( struct qserver *server ) -{ - return send_packet( server, server->type->status_packet, server->type->status_len ); -} - -query_status_t deal_with_tee_packet( struct qserver *server, char *rawpkt, int pktlen ) -{ - // skip unimplemented ack, crc, etc - char *pkt = rawpkt + 6; - char *tok = NULL, *version = NULL; - int i; - struct player* player; - - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - - if (0 == memcmp( pkt, tee_serverinfo, 8)) - { - pkt += 8; - // version - version = strdup(pkt); pkt += strlen(pkt) + 1; - // server name - server->server_name = strdup(pkt); pkt += strlen(pkt) + 1; - // map name - server->map_name = strdup(pkt); pkt += strlen(pkt) + 1; - // game type - switch(atoi(pkt)) { - case 0: - add_rule( server, server->type->game_rule, "dm", NO_FLAGS); - break; - case 1: - add_rule( server, server->type->game_rule, "tdm", NO_FLAGS); - break; - case 2: - add_rule( server, server->type->game_rule, "ctf", NO_FLAGS); - break; - default: - add_rule( server, server->type->game_rule, "unknown", NO_FLAGS); - break; - } - pkt += strlen(pkt) + 1; - pkt += strlen(pkt) + 1; - pkt += strlen(pkt) + 1; - // num players - server->num_players = atoi(pkt); pkt += strlen(pkt) + 1; - // max players - server->max_players = atoi(pkt); pkt += strlen(pkt) + 1; - // players - for(i = 0; i < server->num_players; i++) - { - player = add_player( server, i ); - player->name = strdup(pkt); pkt += strlen(pkt) + 1; - player->score = atoi(pkt); pkt += strlen(pkt) + 1; - } - // version reprise - server->protocol_version = 0; - - if (NULL == (tok = strtok(version, "."))) return -1; - server->protocol_version |= (atoi(tok) & 0x000F) << 12; - if (NULL == (tok = strtok(NULL, "."))) return -1; - server->protocol_version |= (atoi(tok) & 0x000F) << 8; - if (NULL == (tok = strtok(NULL, "."))) return -1; - server->protocol_version |= (atoi(tok) & 0x00FF); - - free(version); - - return DONE_FORCE; - } - - // unknown packet type - return PKT_ERROR; -} - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + +char tee_serverstatus[14] = { '\x20', '\0', '\0', '\0', '\0', '\0', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', 'f' }; + + + { + /* Teeworlds */ + TEE_SERVER, /* id */ + "TEE", /* type_prefix */ + "tee", /* type_string */ + "-tee", /* type_option */ + "Teeworlds", /* game_name */ + 0, /* master */ + 35515, /* default_port */ + 0, /* port_offset */ + 0, /* flags */ + "gametype", /* game_rule */ + "TEE", /* template_var */ + tee_serverstatus, /* status_packet */ + sizeof(tee_serverstatus), /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_tee_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_tee_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_tee_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_tee_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_tee_packet, /* packet_func */ +}, + + + +/* + * qstat 2.11 + * by Steve Jankowski + * + * Teeworlds protocol + * Copyright 2008 ? Emiliano Leporati + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + * + */ + +#include +#include +#include + +#include "debug.h" +#include "qstat.h" +#include "packet_manip.h" + +char tee_serverinfo[8] = { '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o' }; + +query_status_t send_tee_request_packet( struct qserver *server ) +{ + return send_packet( server, server->type->status_packet, server->type->status_len ); +} + +query_status_t deal_with_tee_packet( struct qserver *server, char *rawpkt, int pktlen ) +{ + // skip unimplemented ack, crc, etc + char *pkt = rawpkt + 6; + char *tok = NULL, *version = NULL; + int i; + struct player* player; + + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + + if (0 == memcmp( pkt, tee_serverinfo, 8)) + { + pkt += 8; + // version + version = strdup(pkt); pkt += strlen(pkt) + 1; + // server name + server->server_name = strdup(pkt); pkt += strlen(pkt) + 1; + // map name + server->map_name = strdup(pkt); pkt += strlen(pkt) + 1; + // game type + switch(atoi(pkt)) { + case 0: + add_rule( server, server->type->game_rule, "dm", NO_FLAGS); + break; + case 1: + add_rule( server, server->type->game_rule, "tdm", NO_FLAGS); + break; + case 2: + add_rule( server, server->type->game_rule, "ctf", NO_FLAGS); + break; + default: + add_rule( server, server->type->game_rule, "unknown", NO_FLAGS); + break; + } + pkt += strlen(pkt) + 1; + pkt += strlen(pkt) + 1; + pkt += strlen(pkt) + 1; + // num players + server->num_players = atoi(pkt); pkt += strlen(pkt) + 1; + // max players + server->max_players = atoi(pkt); pkt += strlen(pkt) + 1; + // players + for(i = 0; i < server->num_players; i++) + { + player = add_player( server, i ); + player->name = strdup(pkt); pkt += strlen(pkt) + 1; + player->score = atoi(pkt); pkt += strlen(pkt) + 1; + } + // version reprise + server->protocol_version = 0; + + if (NULL == (tok = strtok(version, "."))) return -1; + server->protocol_version |= (atoi(tok) & 0x000F) << 12; + if (NULL == (tok = strtok(NULL, "."))) return -1; + server->protocol_version |= (atoi(tok) & 0x000F) << 8; + if (NULL == (tok = strtok(NULL, "."))) return -1; + server->protocol_version |= (atoi(tok) & 0x00FF); + + free(version); + + return DONE_FORCE; + } + + // unknown packet type + return PKT_ERROR; +} + diff --git a/reference/tribes/gameq.txt b/reference/tribes/gameq.txt index 2c7d897..cb02a30 100644 --- a/reference/tribes/gameq.txt +++ b/reference/tribes/gameq.txt @@ -1,63 +1,63 @@ -. - * - * $Id: tribes.php,v 1.1 2007/07/07 14:20:21 tombuskens Exp $ - */ - -[tribes] -status = "b++" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Tribes protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_tribes extends GameQ_Protocol -{ - public function status() - { - // Header - if ($this->p->read(4) != 'c++b') { - throw new GameQ_ParsingException($this->p); - } - - // Variables - $this->r->add('game', $this->p->readPascalString()); - $this->r->add('version', $this->p->readPascalString()); - $this->r->add('hostname', $this->p->readPascalString()); - $this->r->add('dedicated', $this->p->readInt8()); - $this->r->add('password', $this->p->readInt8()); - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('max_players', $this->p->readInt8()); - $this->r->add('cpu_lsb', $this->p->readInt8()); - $this->r->add('cpu_msb', $this->p->readInt8()); - $this->r->add('mod', $this->p->readPascalString()); - $this->r->add('gametype', $this->p->readPascalString()); - $this->r->add('map', $this->p->readPascalString()); - $this->r->add('motd', $this->p->readPascalString()); - $this->r->add('teamcount', $this->p->readInt8()); // Not sure - - // TODO: player listing - } -} -?> - +. + * + * $Id: tribes.php,v 1.1 2007/07/07 14:20:21 tombuskens Exp $ + */ + +[tribes] +status = "b++" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Tribes protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_tribes extends GameQ_Protocol +{ + public function status() + { + // Header + if ($this->p->read(4) != 'c++b') { + throw new GameQ_ParsingException($this->p); + } + + // Variables + $this->r->add('game', $this->p->readPascalString()); + $this->r->add('version', $this->p->readPascalString()); + $this->r->add('hostname', $this->p->readPascalString()); + $this->r->add('dedicated', $this->p->readInt8()); + $this->r->add('password', $this->p->readInt8()); + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('max_players', $this->p->readInt8()); + $this->r->add('cpu_lsb', $this->p->readInt8()); + $this->r->add('cpu_msb', $this->p->readInt8()); + $this->r->add('mod', $this->p->readPascalString()); + $this->r->add('gametype', $this->p->readPascalString()); + $this->r->add('map', $this->p->readPascalString()); + $this->r->add('motd', $this->p->readPascalString()); + $this->r->add('teamcount', $this->p->readInt8()); // Not sure + + // TODO: player listing + } +} +?> + diff --git a/reference/tribes/lgsl.txt b/reference/tribes/lgsl.txt index b792816..ca7f5c6 100644 --- a/reference/tribes/lgsl.txt +++ b/reference/tribes/lgsl.txt @@ -1,125 +1,125 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_23(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: -// http://siteinthe.us -// http://www.tribesmasterserver.com - - fwrite($lgsl_fp, "b++"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - - $buffer = substr($buffer, 4); // REMOVE HEADER - -//---------------------------------------------------------+ - - $server['s']['game'] = lgsl_cut_pascal($buffer); - $server['e']['version'] = lgsl_cut_pascal($buffer); - $server['s']['name'] = lgsl_cut_pascal($buffer); - $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['cpu'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['e']['mod'] = lgsl_cut_pascal($buffer); - $server['e']['type'] = lgsl_cut_pascal($buffer); - $server['s']['map'] = lgsl_cut_pascal($buffer); - $server['e']['motd'] = lgsl_cut_pascal($buffer); - $server['e']['teams'] = ord(lgsl_cut_byte($buffer, 1)); - -//---------------------------------------------------------+ - - $team_field = "?".lgsl_cut_pascal($buffer); - $team_field = split("\t", $team_field); - - foreach ($team_field as $key => $value) - { - $value = substr($value, 1); - $value = strtolower($value); - $team_field[$key] = $value; - } - -//---------------------------------------------------------+ - - $player_field = "?".lgsl_cut_pascal($buffer); - $player_field = split("\t", $player_field); - - foreach ($player_field as $key => $value) - { - $value = substr($value, 1); - $value = strtolower($value); - - if ($value == "player name") { $value = "name"; } - - $player_field[$key] = $value; - } - - $player_field[] = "unknown_1"; - $player_field[] = "unknown_2"; - -//---------------------------------------------------------+ - - for ($i=0; $i<$server['e']['teams']; $i++) - { - $team_name = lgsl_cut_pascal($buffer); - $team_info = lgsl_cut_pascal($buffer); - - if (!$team_info) { continue; } - - $team_info = str_replace("%t", $team_name, $team_info); - $team_info = split("\t", $team_info); - - foreach ($team_info as $key => $value) - { - $field = $team_field[$key]; - $value = trim($value); - - if ($field == "team name") { $field = "name"; } - - $server['t'][$i][$field] = $value; - } - } - -//---------------------------------------------------------+ - - for ($i=0; $i<$server['s']['players']; $i++) - { - $player_bits = array(); - $player_bits[] = ord(lgsl_cut_byte($buffer, 1)) * 4; // %p = PING - $player_bits[] = ord(lgsl_cut_byte($buffer, 1)); // %l = PACKET LOSS - $player_bits[] = ord(lgsl_cut_byte($buffer, 1)); // %t = TEAM - $player_bits[] = lgsl_cut_pascal($buffer); // %n = PLAYER NAME - $player_info = lgsl_cut_pascal($buffer); - - if (!$player_info) { continue; } - - $player_info = str_replace(array("%p","%l","%t","%n"), $player_bits, $player_info); - $player_info = split("\t", $player_info); - - foreach ($player_info as $key => $value) - { - $field = $player_field[$key]; - $value = trim($value); - - if ($field == "team") { $value = $server['t'][$value]['name']; } - - $server['p'][$i][$field] = $value; - } - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + function lgsl_query_23(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: +// http://siteinthe.us +// http://www.tribesmasterserver.com + + fwrite($lgsl_fp, "b++"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + + $buffer = substr($buffer, 4); // REMOVE HEADER + +//---------------------------------------------------------+ + + $server['s']['game'] = lgsl_cut_pascal($buffer); + $server['e']['version'] = lgsl_cut_pascal($buffer); + $server['s']['name'] = lgsl_cut_pascal($buffer); + $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['cpu'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['e']['mod'] = lgsl_cut_pascal($buffer); + $server['e']['type'] = lgsl_cut_pascal($buffer); + $server['s']['map'] = lgsl_cut_pascal($buffer); + $server['e']['motd'] = lgsl_cut_pascal($buffer); + $server['e']['teams'] = ord(lgsl_cut_byte($buffer, 1)); + +//---------------------------------------------------------+ + + $team_field = "?".lgsl_cut_pascal($buffer); + $team_field = split("\t", $team_field); + + foreach ($team_field as $key => $value) + { + $value = substr($value, 1); + $value = strtolower($value); + $team_field[$key] = $value; + } + +//---------------------------------------------------------+ + + $player_field = "?".lgsl_cut_pascal($buffer); + $player_field = split("\t", $player_field); + + foreach ($player_field as $key => $value) + { + $value = substr($value, 1); + $value = strtolower($value); + + if ($value == "player name") { $value = "name"; } + + $player_field[$key] = $value; + } + + $player_field[] = "unknown_1"; + $player_field[] = "unknown_2"; + +//---------------------------------------------------------+ + + for ($i=0; $i<$server['e']['teams']; $i++) + { + $team_name = lgsl_cut_pascal($buffer); + $team_info = lgsl_cut_pascal($buffer); + + if (!$team_info) { continue; } + + $team_info = str_replace("%t", $team_name, $team_info); + $team_info = split("\t", $team_info); + + foreach ($team_info as $key => $value) + { + $field = $team_field[$key]; + $value = trim($value); + + if ($field == "team name") { $field = "name"; } + + $server['t'][$i][$field] = $value; + } + } + +//---------------------------------------------------------+ + + for ($i=0; $i<$server['s']['players']; $i++) + { + $player_bits = array(); + $player_bits[] = ord(lgsl_cut_byte($buffer, 1)) * 4; // %p = PING + $player_bits[] = ord(lgsl_cut_byte($buffer, 1)); // %l = PACKET LOSS + $player_bits[] = ord(lgsl_cut_byte($buffer, 1)); // %t = TEAM + $player_bits[] = lgsl_cut_pascal($buffer); // %n = PLAYER NAME + $player_info = lgsl_cut_pascal($buffer); + + if (!$player_info) { continue; } + + $player_info = str_replace(array("%p","%l","%t","%n"), $player_bits, $player_info); + $player_info = split("\t", $player_info); + + foreach ($player_info as $key => $value) + { + $field = $player_field[$key]; + $value = trim($value); + + if ($field == "team") { $value = $server['t'][$value]['name']; } + + $server['p'][$i][$field] = $value; + } + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/tribes/qstat.txt b/reference/tribes/qstat.txt index 8bd0be3..04eb5a8 100644 --- a/reference/tribes/qstat.txt +++ b/reference/tribes/qstat.txt @@ -1,247 +1,247 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - -/* TRIBES */ -char tribes_info[] = { '`', '*', '*' }; -char tribes_players[] = { 'b', '*', '*' }; -/* This is what the game sends to get minimal status -{ '\020', '\03', '\377', 0, (unsigned char)0xc5, 6 }; -*/ -char tribes_info_reponse[] = { 'a', '*', '*', 'b' }; -char tribes_players_reponse[] = { 'c', '*', '*', 'b' }; -char tribes_masterquery[] = { 0x10, 0x3, '\377', 0, 0x2 }; -char tribes_master_response[] = { 0x10, 0x6 }; - - - - { - /* TRIBES */ - TRIBES_SERVER, /* id */ - "TBS", /* type_prefix */ - "tbs", /* type_string */ - "-tbs", /* type_option */ - "Tribes", /* game_name */ - 0, /* master */ - TRIBES_DEFAULT_PORT, /* default_port */ - 0, /* port_offset */ - TF_SINGLE_QUERY, /* flags */ - "game", /* game_rule */ - "TRIBES", /* template_var */ - (char*) &tribes_info, /* status_packet */ - sizeof( tribes_info), /* status_len */ - (char*) &tribes_players, /* player_packet */ - sizeof( tribes_players), /* player_len */ - (char*) &tribes_players, /* rule_packet */ - sizeof( tribes_players), /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_tribes_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_tribes_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_tribes_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_tribes_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_tribes_packet, /* packet_func */ -}, - - - - - - -query_status_t deal_with_tribes_packet(struct qserver *server, char *rawpkt, int pktlen) -{ - unsigned char *pkt, *end; - int len, pnum, ping, packet_loss, n_teams, t; - struct player *player; - struct player **teams = NULL; - struct player **last_player = &server->players; - char buf[24]; - - debug( 2, "deal_with_tribes_packet %p, %d", server, pktlen ); - - if (server->server_name == NULL) - { - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - } - else - { - gettimeofday(&server->packet_time1, NULL); - } - - if (pktlen < sizeof(tribes_info_reponse)) - { - return PKT_ERROR; - } - - if (strncmp(rawpkt, tribes_players_reponse, sizeof(tribes_players_reponse)) != 0) - { - return PKT_ERROR; - } - - pkt = (unsigned char*) &rawpkt[sizeof(tribes_info_reponse)]; - - len = *pkt; /* game name: "Tribes" */ - add_nrule(server, "gamename", (char*)pkt + 1, len); - pkt += len + 1; - len = *pkt; /* version */ - add_nrule(server, "version", (char*)pkt + 1, len); - pkt += len + 1; - len = *pkt; /* server name */ - server->server_name = strndup((char*)pkt + 1, len); - pkt += len + 1; - add_rule(server, "dedicated", *pkt ? "1" : "0", NO_FLAGS); - pkt++; /* flag: dedicated server */ - add_rule(server, "needpass", *pkt ? "1" : "0", NO_FLAGS); - pkt++; /* flag: password on server */ - server->num_players = *pkt++; - server->max_players = *pkt++; - - sprintf(buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1] *256); - add_rule(server, "cpu", buf, NO_FLAGS); - pkt++; /* cpu speed, lsb */ - pkt++; /* cpu speed, msb */ - - len = *pkt; /* Mod (game) */ - add_nrule(server, "mods", (char*)pkt + 1, len); - pkt += len + 1; - - len = *pkt; /* game (mission): "C&H" */ - add_nrule(server, "game", (char*)pkt + 1, len); - pkt += len + 1; - - len = *pkt; /* Mission (map) */ - server->map_name = strndup((char*)pkt + 1, len); - pkt += len + 1; - - len = *pkt; /* description (contains Admin: and Email: ) */ - debug( 2, "%.*s\n", len, pkt + 1); - pkt += len + 1; - - n_teams = *pkt++; /* number of teams */ - if (n_teams == 255) - { - return PKT_ERROR; - } - sprintf(buf, "%d", n_teams); - add_rule(server, "numteams", buf, NO_FLAGS); - - len = *pkt; /* first title */ - debug( 2, "%.*s\n", len, pkt + 1); - pkt += len + 1; - - len = *pkt; /* second title */ - debug( 2, "%.*s\n", len, pkt + 1); - pkt += len + 1; - - if (n_teams > 1) - { - teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams); - for (t = 0; t < n_teams; t++) - { - teams[t] = (struct player*)calloc(1, sizeof(struct player)); - teams[t]->number = TRIBES_TEAM; - teams[t]->team = t; - len = *pkt; /* team name */ - teams[t]->name = strndup((char*)pkt + 1, len); - debug( 2, "team#0 <%.*s>\n", len, pkt + 1); - pkt += len + 1; - - len = *pkt; /* team score */ - if (len > 2) - { - strncpy(buf, (char*)pkt + 1+3, len - 3); - buf[len - 3] = '\0'; - } - else - { - debug( 2, "%s score len %d\n", server->arg, len); - buf[0] = '\0'; - } - teams[t]->frags = atoi(buf); - debug( 2, "team#0 <%.*s>\n", len - 3, pkt + 1+3); - pkt += len + 1; - } - } - else - { - len = *pkt; /* DM team? */ - debug( 2, "%.*s\n", len, pkt + 1); - pkt += len + 1; - pkt++; - n_teams = 0; - } - - pnum = 0; - while ((char*)pkt < (rawpkt + pktlen)) - { - ping = (unsigned int) *pkt << 2; - pkt++; - packet_loss = *pkt; - pkt++; - debug( 2, "player#%d, team #%d\n", pnum, (int) *pkt); - pkt++; - len = *pkt; - if ((char*)pkt + len > (rawpkt + pktlen)) - { - break; - } - player = (struct player*)calloc(1, sizeof(struct player)); - player->team = pkt[ - 1]; - if (n_teams && player->team < n_teams) - { - player->team_name = teams[player->team]->name; - } - else if (player->team == 255 && n_teams) - { - player->team_name = "Unknown"; - } - player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; - player->ping = ping; - player->packet_loss = packet_loss; - player->name = strndup((char*)pkt + 1, len); - debug( 2, "player#%d, name %.*s\n", pnum, len, pkt + 1); - pkt += len + 1; - len = *pkt; - debug( 2, "player#%d, info <%.*s>\n", pnum, len, pkt + 1); - end = (unsigned char*)strchr((char*)pkt + 9, 0x9); - if (end) - { - strncpy(buf, (char*)pkt + 9, end - (pkt + 9)); - buf[end - (pkt + 9)] = '\0'; - player->frags = atoi(buf); - debug( 2, "player#%d, score <%.*s>\n", pnum, (unsigned)(end - (pkt + 9)), pkt + 9); - } - - *last_player = player; - last_player = &player->next; - - pkt += len + 1; - pnum++; - } - - for (t = n_teams; t;) - { - t--; - teams[t]->next = server->players; - server->players = teams[t]; - } - free(teams); - - return DONE_AUTO; -} +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + +/* TRIBES */ +char tribes_info[] = { '`', '*', '*' }; +char tribes_players[] = { 'b', '*', '*' }; +/* This is what the game sends to get minimal status +{ '\020', '\03', '\377', 0, (unsigned char)0xc5, 6 }; +*/ +char tribes_info_reponse[] = { 'a', '*', '*', 'b' }; +char tribes_players_reponse[] = { 'c', '*', '*', 'b' }; +char tribes_masterquery[] = { 0x10, 0x3, '\377', 0, 0x2 }; +char tribes_master_response[] = { 0x10, 0x6 }; + + + + { + /* TRIBES */ + TRIBES_SERVER, /* id */ + "TBS", /* type_prefix */ + "tbs", /* type_string */ + "-tbs", /* type_option */ + "Tribes", /* game_name */ + 0, /* master */ + TRIBES_DEFAULT_PORT, /* default_port */ + 0, /* port_offset */ + TF_SINGLE_QUERY, /* flags */ + "game", /* game_rule */ + "TRIBES", /* template_var */ + (char*) &tribes_info, /* status_packet */ + sizeof( tribes_info), /* status_len */ + (char*) &tribes_players, /* player_packet */ + sizeof( tribes_players), /* player_len */ + (char*) &tribes_players, /* rule_packet */ + sizeof( tribes_players), /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_tribes_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_tribes_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_tribes_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_tribes_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_tribes_packet, /* packet_func */ +}, + + + + + + +query_status_t deal_with_tribes_packet(struct qserver *server, char *rawpkt, int pktlen) +{ + unsigned char *pkt, *end; + int len, pnum, ping, packet_loss, n_teams, t; + struct player *player; + struct player **teams = NULL; + struct player **last_player = &server->players; + char buf[24]; + + debug( 2, "deal_with_tribes_packet %p, %d", server, pktlen ); + + if (server->server_name == NULL) + { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } + else + { + gettimeofday(&server->packet_time1, NULL); + } + + if (pktlen < sizeof(tribes_info_reponse)) + { + return PKT_ERROR; + } + + if (strncmp(rawpkt, tribes_players_reponse, sizeof(tribes_players_reponse)) != 0) + { + return PKT_ERROR; + } + + pkt = (unsigned char*) &rawpkt[sizeof(tribes_info_reponse)]; + + len = *pkt; /* game name: "Tribes" */ + add_nrule(server, "gamename", (char*)pkt + 1, len); + pkt += len + 1; + len = *pkt; /* version */ + add_nrule(server, "version", (char*)pkt + 1, len); + pkt += len + 1; + len = *pkt; /* server name */ + server->server_name = strndup((char*)pkt + 1, len); + pkt += len + 1; + add_rule(server, "dedicated", *pkt ? "1" : "0", NO_FLAGS); + pkt++; /* flag: dedicated server */ + add_rule(server, "needpass", *pkt ? "1" : "0", NO_FLAGS); + pkt++; /* flag: password on server */ + server->num_players = *pkt++; + server->max_players = *pkt++; + + sprintf(buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1] *256); + add_rule(server, "cpu", buf, NO_FLAGS); + pkt++; /* cpu speed, lsb */ + pkt++; /* cpu speed, msb */ + + len = *pkt; /* Mod (game) */ + add_nrule(server, "mods", (char*)pkt + 1, len); + pkt += len + 1; + + len = *pkt; /* game (mission): "C&H" */ + add_nrule(server, "game", (char*)pkt + 1, len); + pkt += len + 1; + + len = *pkt; /* Mission (map) */ + server->map_name = strndup((char*)pkt + 1, len); + pkt += len + 1; + + len = *pkt; /* description (contains Admin: and Email: ) */ + debug( 2, "%.*s\n", len, pkt + 1); + pkt += len + 1; + + n_teams = *pkt++; /* number of teams */ + if (n_teams == 255) + { + return PKT_ERROR; + } + sprintf(buf, "%d", n_teams); + add_rule(server, "numteams", buf, NO_FLAGS); + + len = *pkt; /* first title */ + debug( 2, "%.*s\n", len, pkt + 1); + pkt += len + 1; + + len = *pkt; /* second title */ + debug( 2, "%.*s\n", len, pkt + 1); + pkt += len + 1; + + if (n_teams > 1) + { + teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams); + for (t = 0; t < n_teams; t++) + { + teams[t] = (struct player*)calloc(1, sizeof(struct player)); + teams[t]->number = TRIBES_TEAM; + teams[t]->team = t; + len = *pkt; /* team name */ + teams[t]->name = strndup((char*)pkt + 1, len); + debug( 2, "team#0 <%.*s>\n", len, pkt + 1); + pkt += len + 1; + + len = *pkt; /* team score */ + if (len > 2) + { + strncpy(buf, (char*)pkt + 1+3, len - 3); + buf[len - 3] = '\0'; + } + else + { + debug( 2, "%s score len %d\n", server->arg, len); + buf[0] = '\0'; + } + teams[t]->frags = atoi(buf); + debug( 2, "team#0 <%.*s>\n", len - 3, pkt + 1+3); + pkt += len + 1; + } + } + else + { + len = *pkt; /* DM team? */ + debug( 2, "%.*s\n", len, pkt + 1); + pkt += len + 1; + pkt++; + n_teams = 0; + } + + pnum = 0; + while ((char*)pkt < (rawpkt + pktlen)) + { + ping = (unsigned int) *pkt << 2; + pkt++; + packet_loss = *pkt; + pkt++; + debug( 2, "player#%d, team #%d\n", pnum, (int) *pkt); + pkt++; + len = *pkt; + if ((char*)pkt + len > (rawpkt + pktlen)) + { + break; + } + player = (struct player*)calloc(1, sizeof(struct player)); + player->team = pkt[ - 1]; + if (n_teams && player->team < n_teams) + { + player->team_name = teams[player->team]->name; + } + else if (player->team == 255 && n_teams) + { + player->team_name = "Unknown"; + } + player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; + player->ping = ping; + player->packet_loss = packet_loss; + player->name = strndup((char*)pkt + 1, len); + debug( 2, "player#%d, name %.*s\n", pnum, len, pkt + 1); + pkt += len + 1; + len = *pkt; + debug( 2, "player#%d, info <%.*s>\n", pnum, len, pkt + 1); + end = (unsigned char*)strchr((char*)pkt + 9, 0x9); + if (end) + { + strncpy(buf, (char*)pkt + 9, end - (pkt + 9)); + buf[end - (pkt + 9)] = '\0'; + player->frags = atoi(buf); + debug( 2, "player#%d, score <%.*s>\n", pnum, (unsigned)(end - (pkt + 9)), pkt + 9); + } + + *last_player = player; + last_player = &player->next; + + pkt += len + 1; + pnum++; + } + + for (t = n_teams; t;) + { + t--; + teams[t]->next = server->players; + server->players = teams[t]; + } + free(teams); + + return DONE_AUTO; +} diff --git a/reference/tribes2/gameq.txt b/reference/tribes2/gameq.txt index 2f51873..69d2d73 100644 --- a/reference/tribes2/gameq.txt +++ b/reference/tribes2/gameq.txt @@ -1,104 +1,104 @@ -. - * - * $Id: tribes2.php,v 1.1 2007/07/07 14:52:01 tombuskens Exp $ - */ - -[tribes2] -info = "\x0E\x02\x01\x02\x03\x04" -status = "\x12\x02\x01\x02\x03\x04" - - -require_once GAMEQ_BASE . 'Protocol.php'; - - -/** - * Tribes 2 protocol - * - * @author Tom Buskens - * @version $Revision: 1.1 $ - */ -class GameQ_Protocol_tribes2 extends GameQ_Protocol -{ - public function info() - { - // Header - $this->p->skip(6); - - $this->r->add('version', $this->p->readPascalString()); - - // TODO: Protocol and build numbers - $this->p->skip(12); - - $this->r->add('hostname', $this->p->readPascalString()); - } - - public function status() - { - // Header - $this->p->skip(6); - - // Vars - $this->r->add('mod', $this->p->readPascalString()); - $this->r->add('gametype', $this->p->readPascalString()); - $this->r->add('map', $this->p->readPascalString()); - $this->readBitflag($this->p->read()); - $this->r->add('num_players', $this->p->readInt8()); - $this->r->add('max_players', $this->p->readInt8()); - $this->r->add('num_bots', $this->p->readInt8()); - $this->r->add('cpu', $this->p->readInt16()); - $this->r->add('info', $this->p->readPascalString()); - - $this->p->skip(2); - - $this->teams(); - $this->players(); - } - - private function teams() - { - $num_teams = $this->p->read(); - $this->r->add('num_teams', $num_teams); - $this->p->skip(); - - for ($i = 0; $i < $num_teams; $i++) { - $this->r->addTeam('name', $this->p->readString("\x09")); - $this->r->addTeam('score', $this->p->readString("\x0a")); - } - } - - private function players() - { - // TODO - } - - - private function readBitflag($flag) - { - $vars = array('dedicated', 'password', 'linux', - 'tournament', 'no_alias'); - - $bit = 1; - foreach ($vars as $var) { - $value = ($flag & $bit) ? 1 : 0; - $this->r->add($var, $value); - $bit *= 2; - } - } -} -?> - +. + * + * $Id: tribes2.php,v 1.1 2007/07/07 14:52:01 tombuskens Exp $ + */ + +[tribes2] +info = "\x0E\x02\x01\x02\x03\x04" +status = "\x12\x02\x01\x02\x03\x04" + + +require_once GAMEQ_BASE . 'Protocol.php'; + + +/** + * Tribes 2 protocol + * + * @author Tom Buskens + * @version $Revision: 1.1 $ + */ +class GameQ_Protocol_tribes2 extends GameQ_Protocol +{ + public function info() + { + // Header + $this->p->skip(6); + + $this->r->add('version', $this->p->readPascalString()); + + // TODO: Protocol and build numbers + $this->p->skip(12); + + $this->r->add('hostname', $this->p->readPascalString()); + } + + public function status() + { + // Header + $this->p->skip(6); + + // Vars + $this->r->add('mod', $this->p->readPascalString()); + $this->r->add('gametype', $this->p->readPascalString()); + $this->r->add('map', $this->p->readPascalString()); + $this->readBitflag($this->p->read()); + $this->r->add('num_players', $this->p->readInt8()); + $this->r->add('max_players', $this->p->readInt8()); + $this->r->add('num_bots', $this->p->readInt8()); + $this->r->add('cpu', $this->p->readInt16()); + $this->r->add('info', $this->p->readPascalString()); + + $this->p->skip(2); + + $this->teams(); + $this->players(); + } + + private function teams() + { + $num_teams = $this->p->read(); + $this->r->add('num_teams', $num_teams); + $this->p->skip(); + + for ($i = 0; $i < $num_teams; $i++) { + $this->r->addTeam('name', $this->p->readString("\x09")); + $this->r->addTeam('score', $this->p->readString("\x0a")); + } + } + + private function players() + { + // TODO + } + + + private function readBitflag($flag) + { + $vars = array('dedicated', 'password', 'linux', + 'tournament', 'no_alias'); + + $bit = 1; + foreach ($vars as $var) { + $value = ($flag & $bit) ? 1 : 0; + $this->r->add($var, $value); + $bit *= 2; + } + } +} +?> + diff --git a/reference/tribes2/lgsl.txt b/reference/tribes2/lgsl.txt index cf4373c..3c1c1d8 100644 --- a/reference/tribes2/lgsl.txt +++ b/reference/tribes2/lgsl.txt @@ -1,72 +1,72 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - - function lgsl_query_25(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: http://www.tribesnext.com - - fwrite($lgsl_fp,"\x12\x02\x21\x21\x21\x21"); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - - $buffer = substr($buffer, 6); // REMOVE HEADER - -//---------------------------------------------------------+ - - $server['s']['game'] = lgsl_cut_pascal($buffer); - $server['e']['gamemode'] = lgsl_cut_pascal($buffer); - $server['s']['map'] = lgsl_cut_pascal($buffer); - $server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['bots'] = ord(lgsl_cut_byte($buffer, 1)); - $server['e']['cpu'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['e']['motd'] = lgsl_cut_pascal($buffer); - $server['e']['unknown'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - - $server['e']['dedicated'] = ($server['e']['bit_flags'] & 1) ? "1" : "0"; - $server['s']['password'] = ($server['e']['bit_flags'] & 2) ? "1" : "0"; - $server['e']['os'] = ($server['e']['bit_flags'] & 4) ? "L" : "W"; - $server['e']['tournament'] = ($server['e']['bit_flags'] & 8) ? "1" : "0"; - $server['e']['no_alias'] = ($server['e']['bit_flags'] & 16) ? "1" : "0"; - - unset($server['e']['bit_flags']); - -//---------------------------------------------------------+ - - $team_total = lgsl_cut_string($buffer, 0, "\x0A"); - - for ($i=0; $i<$team_total; $i++) - { - $server['t'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x09"); - $server['t'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A"); - } - - $player_total = lgsl_cut_string($buffer, 0, "\x0A"); - - for ($i=0; $i<$player_total; $i++) - { - lgsl_cut_byte($buffer, 1); // ? 16 - lgsl_cut_byte($buffer, 1); // ? 8 or 14 = BOT / 12 = ALIAS / 11 = NORMAL - if (ord($buffer[0]) < 32) { lgsl_cut_byte($buffer, 1); } // ? 8 PREFIXES SOME NAMES - - $server['p'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x11"); - lgsl_cut_string($buffer, 0, "\x09"); // ALWAYS BLANK - $server['p'][$i]['team'] = lgsl_cut_string($buffer, 0, "\x09"); - $server['p'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A"); - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + + function lgsl_query_25(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: http://www.tribesnext.com + + fwrite($lgsl_fp,"\x12\x02\x21\x21\x21\x21"); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + + $buffer = substr($buffer, 6); // REMOVE HEADER + +//---------------------------------------------------------+ + + $server['s']['game'] = lgsl_cut_pascal($buffer); + $server['e']['gamemode'] = lgsl_cut_pascal($buffer); + $server['s']['map'] = lgsl_cut_pascal($buffer); + $server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['bots'] = ord(lgsl_cut_byte($buffer, 1)); + $server['e']['cpu'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['e']['motd'] = lgsl_cut_pascal($buffer); + $server['e']['unknown'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + + $server['e']['dedicated'] = ($server['e']['bit_flags'] & 1) ? "1" : "0"; + $server['s']['password'] = ($server['e']['bit_flags'] & 2) ? "1" : "0"; + $server['e']['os'] = ($server['e']['bit_flags'] & 4) ? "L" : "W"; + $server['e']['tournament'] = ($server['e']['bit_flags'] & 8) ? "1" : "0"; + $server['e']['no_alias'] = ($server['e']['bit_flags'] & 16) ? "1" : "0"; + + unset($server['e']['bit_flags']); + +//---------------------------------------------------------+ + + $team_total = lgsl_cut_string($buffer, 0, "\x0A"); + + for ($i=0; $i<$team_total; $i++) + { + $server['t'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x09"); + $server['t'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A"); + } + + $player_total = lgsl_cut_string($buffer, 0, "\x0A"); + + for ($i=0; $i<$player_total; $i++) + { + lgsl_cut_byte($buffer, 1); // ? 16 + lgsl_cut_byte($buffer, 1); // ? 8 or 14 = BOT / 12 = ALIAS / 11 = NORMAL + if (ord($buffer[0]) < 32) { lgsl_cut_byte($buffer, 1); } // ? 8 PREFIXES SOME NAMES + + $server['p'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x11"); + lgsl_cut_string($buffer, 0, "\x09"); // ALWAYS BLANK + $server['p'][$i]['team'] = lgsl_cut_string($buffer, 0, "\x09"); + $server['p'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A"); + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/tribes2/qstat.txt b/reference/tribes2/qstat.txt index 22dd553..0ea819c 100644 --- a/reference/tribes2/qstat.txt +++ b/reference/tribes2/qstat.txt @@ -1,455 +1,455 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - -/* TRIBES 2 */ -#define TRIBES2_QUERY_GAME_TYPES 2 -#define TRIBES2_QUERY_MASTER 6 -#define TRIBES2_QUERY_PING 14 -#define TRIBES2_QUERY_INFO 18 -#define TRIBES2_RESPONSE_GAME_TYPES 4 -#define TRIBES2_RESPONSE_MASTER 8 -#define TRIBES2_RESPONSE_PING 16 -#define TRIBES2_RESPONSE_INFO 20 - -#define TRIBES2_NO_COMPRESS 2 -#define TRIBES2_DEFAULT_PACKET_INDEX 255 -#define TRIBES2_STATUS_DEDICATED (1<<0) -#define TRIBES2_STATUS_PASSWORD (1<<1) -#define TRIBES2_STATUS_LINUX (1<<2) -#define TRIBES2_STATUS_TOURNAMENT (1<<3) -#define TRIBES2_STATUS_NOALIAS (1<<4) -#define TRIBES2_STATUS_TEAMDAMAGE (1<<5) -#define TRIBES2_STATUS_TOURNAMENT_VER3 (1<<6) -#define TRIBES2_STATUS_NOALIAS_VER3 (1<<7) -char tribes2_game_types_request[] = { TRIBES2_QUERY_GAME_TYPES, 0, 1,2,3,4 }; -char tribes2_ping[] = { TRIBES2_QUERY_PING, TRIBES2_NO_COMPRESS, 1,2,3,4 }; -char tribes2_info[] = { TRIBES2_QUERY_INFO, TRIBES2_NO_COMPRESS, 1,2,3,4 }; -unsigned char tribes2_masterquery[] = { - TRIBES2_QUERY_MASTER, 128, /* <= build 22228, this was 0 */ - 11,12,13,14, - 255, - 3, 'a', 'n', 'y', - 3, 'a', 'n', 'y', - 0, 255, /* min/max players */ - 0xff, 0xff, 0xff, 0xff, /* region mask */ - 0, 0, 0, 0, /* build version */ - 0, /* status */ - 255, /* max bots */ - 0, 0, /* min cpu */ - 0 /* # buddies */ - }; -#define TRIBES2_ID_OFFSET 2 - - - - -{ - /* TRIBES 2 */ - TRIBES2_SERVER, /* id */ - "T2S", /* type_prefix */ - "t2s", /* type_string */ - "-t2s", /* type_option */ - "Tribes 2", /* game_name */ - 0, /* master */ - TRIBES2_DEFAULT_PORT, /* default_port */ - 0, /* port_offset */ - 0, /* flags */ - "game", /* game_rule */ - "TRIBES2", /* template_var */ - (char*) &tribes2_ping, /* status_packet */ - sizeof( tribes2_ping), /* status_len */ - (char*) &tribes2_info, /* player_packet */ - sizeof( tribes2_info), /* player_len */ - (char*) NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_tribes2_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_tribes2_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_tribes2_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_tribes2_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_tribes2_packet, /* packet_func */ -}, - - - - - -query_status_t send_tribes2_request_packet(struct qserver *server) -{ - int rc; - - if (server->flags &FLAG_BROADCAST && server->server_name == NULL) - { - rc = send_broadcast(server, server->type->status_packet, server->type->status_len); - } - else if (server->server_name == NULL) - { - rc = send(server->fd, server->type->status_packet, server->type->status_len, 0); - } - else - { - rc = send(server->fd, server->type->player_packet, server->type->player_len, 0); - } - - if (rc == SOCKET_ERROR) - { - return send_error( server, rc ); - } - - register_send(server); - - return rc; -} - - - - -void get_tribes2_player_type(struct player *player) -{ - char *name = player->name; - for (; *name; name++) - { - switch (*name) - { - case 0x8: - player->type_flag = PLAYER_TYPE_NORMAL; - continue; - case 0xc: - player->type_flag = PLAYER_TYPE_ALIAS; - continue; - case 0xe: - player->type_flag = PLAYER_TYPE_BOT; - continue; - case 0xb: - break; - default: - continue; - } - name++; - if (isprint(*name)) - { - char *n = name; - for (; isprint(*n); n++) - ; - player->tribe_tag = strndup(name, n - name); - name = n; - } - if (! *name) - { - break; - } - } -} - -query_status_t deal_with_tribes2_packet(struct qserver *server, char *pkt, int pktlen) -{ - char str[256], *pktstart = pkt, *term, *start; - unsigned int minimum_net_protocol, build_version, i, t, len, s, status; - unsigned int net_protocol; - unsigned short cpu_speed; - int n_teams = 0, n_players; - struct player **teams = NULL, *player; - struct player **last_player = &server->players; - int query_version; - - debug( 2, "deal_with_tribes2_packet %p, %d", server, pktlen ); - - pkt[pktlen] = '\0'; - - if (server->server_name == NULL) - { - server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); - } - /* - else - gettimeofday( &server->packet_time1, NULL); - */ - - if (pkt[0] == TRIBES2_RESPONSE_PING) - { - if (pkt[6] < 4 || pkt[6] > 12 || strncmp(pkt + 7, "VER", 3) != 0) - { - return PKT_ERROR; - } - - strncpy(str, pkt + 10, pkt[6] - 3); - str[pkt[6] - 3] = '\0'; - query_version = atoi(str); - add_nrule(server, "queryversion", pkt + 7, pkt[6]); - pkt += 7+pkt[6]; - - server->protocol_version = query_version; - if (query_version != 3 && query_version != 5) - { - server->server_name = strdup("Unknown query version"); - return PKT_ERROR; - } - - if (query_version == 5) - { - net_protocol = swap_long_from_little(pkt); - sprintf(str, "%u", net_protocol); - add_rule(server, "net_protocol", str, NO_FLAGS); - pkt += 4; - } - minimum_net_protocol = swap_long_from_little(pkt); - sprintf(str, "%u", minimum_net_protocol); - add_rule(server, "minimum_net_protocol", str, NO_FLAGS); - pkt += 4; - build_version = swap_long_from_little(pkt); - sprintf(str, "%u", build_version); - add_rule(server, "build_version", str, NO_FLAGS); - pkt += 4; - - server->server_name = strndup(pkt + 1, *(unsigned char*)(pkt)); - - /* Always send the player request because the ping packet - * contains very little information */ - send_player_request_packet(server); - return 0; - } - else if (pkt[0] != TRIBES2_RESPONSE_INFO) - { - return PKT_ERROR; - } - - pkt += 6; - for (i = 0; i < *(unsigned char*)pkt; i++) - if (!isprint(pkt[i + 1])) - { - return PKT_ERROR; - } - add_nrule(server, server->type->game_rule, pkt + 1, *(unsigned char*)pkt); - server->game = strndup(pkt + 1, *(unsigned char*)pkt); - pkt += *pkt + 1; - add_nrule(server, "mission", pkt + 1, *(unsigned char*)pkt); - pkt += *pkt + 1; - server->map_name = strndup(pkt + 1, *(unsigned char*)pkt); - pkt += *pkt + 1; - - status = *(unsigned char*)pkt; - sprintf(str, "%u", status); - add_rule(server, "status", str, NO_FLAGS); - if (status &TRIBES2_STATUS_DEDICATED) - { - add_rule(server, "dedicated", "1", NO_FLAGS); - } - if (status &TRIBES2_STATUS_PASSWORD) - { - add_rule(server, "password", "1", NO_FLAGS); - } - if (status &TRIBES2_STATUS_LINUX) - { - add_rule(server, "linux", "1", NO_FLAGS); - } - if (status &TRIBES2_STATUS_TEAMDAMAGE) - { - add_rule(server, "teamdamage", "1", NO_FLAGS); - } - if (server->protocol_version == 3) - { - if (status &TRIBES2_STATUS_TOURNAMENT_VER3) - { - add_rule(server, "tournament", "1", NO_FLAGS); - } - if (status &TRIBES2_STATUS_NOALIAS_VER3) - { - add_rule(server, "no_aliases", "1", NO_FLAGS); - } - } - else - { - if (status &TRIBES2_STATUS_TOURNAMENT) - { - add_rule(server, "tournament", "1", NO_FLAGS); - } - if (status &TRIBES2_STATUS_NOALIAS) - { - add_rule(server, "no_aliases", "1", NO_FLAGS); - } - } - pkt++; - server->num_players = *(unsigned char*)pkt; - pkt++; - server->max_players = *(unsigned char*)pkt; - pkt++; - sprintf(str, "%u", *(unsigned char*)pkt); - add_rule(server, "bot_count", str, NO_FLAGS); - pkt++; - cpu_speed = swap_short_from_little(pkt); - sprintf(str, "%hu", cpu_speed); - add_rule(server, "cpu_speed", str, NO_FLAGS); - pkt += 2; - - if (strcmp(server->server_name, "VER3") == 0) - { - free(server->server_name); - server->server_name = strndup(pkt + 1, *(unsigned char*)pkt); - } - else - { - add_nrule(server, "info", pkt + 1, *(unsigned char*)pkt); - } - - pkt += *(unsigned char*)pkt + 1; - len = swap_short_from_little(pkt); - pkt += 2; - start = pkt; - if (len + (pkt - pktstart) > pktlen) - { - len -= (len + (pkt - pktstart)) - pktlen; - } - - if (len == 0 || pkt - pktstart >= pktlen) - { - goto info_done; - } - - term = strchr(pkt, 0xa); - if (!term) - { - goto info_done; - } - *term = '\0'; - n_teams = atoi(pkt); - sprintf(str, "%d", n_teams); - add_rule(server, "numteams", str, NO_FLAGS); - pkt = term + 1; - - if (pkt - pktstart >= pktlen) - { - goto info_done; - } - - teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams); - for (t = 0; t < n_teams; t++) - { - teams[t] = (struct player*)calloc(1, sizeof(struct player)); - teams[t]->number = TRIBES_TEAM; - teams[t]->team = t; - /* team name */ - term = strchr(pkt, 0x9); - if (!term) - { - n_teams = t; - goto info_done; - } teams[t]->name = strndup(pkt, term - pkt); - pkt = term + 1; - term = strchr(pkt, 0xa); - if (!term) - { - n_teams = t; - goto info_done; - } - *term = '\0'; - teams[t]->frags = atoi(pkt); - pkt = term + 1; - if (pkt - pktstart >= pktlen) - { - goto info_done; - } - } - - term = strchr(pkt, 0xa); - if (!term || term - start >= len) - { - goto info_done; - } - *term = '\0'; - n_players = atoi(pkt); - pkt = term + 1; - - for (i = 0; i < n_players && pkt - start < len; i++) - { - pkt++; /* skip first byte (0x10) */ - if (pkt - start >= len) - { - break; - } - player = (struct player*)calloc(1, sizeof(struct player)); - term = strchr(pkt, 0x11); - if (!term || term - start >= len) - { - free(player); - break; - } player->name = strndup(pkt, term - pkt); - get_tribes2_player_type(player); - pkt = term + 1; - pkt++; /* skip 0x9 */ - if (pkt - start >= len) - { - break; - } - term = strchr(pkt, 0x9); - if (!term || term - start >= len) - { - free(player->name); - free(player); - break; - } - for (t = 0; t < n_teams; t++) - { - if (term - pkt == strlen(teams[t]->name) && strncmp(pkt, teams[t]->name, term - pkt) == 0) - { - break; - } - } - if (t == n_teams) - { - player->team = - 1; - player->team_name = "Unassigned"; - } - else - { - player->team = t; - player->team_name = teams[t]->name; - } - player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; - pkt = term + 1; - for (s = 0; *pkt != 0xa && pkt - start < len; pkt++) - { - str[s++] = *pkt; - } - str[s] = '\0'; - player->frags = atoi(str); - if (*pkt == 0xa) - { - pkt++; - } - - *last_player = player; - last_player = &player->next; - } - -info_done: - for (t = n_teams; t;) - { - t--; - teams[t]->next = server->players; - server->players = teams[t]; - } - if (teams) - { - free(teams); - } - - return DONE_FORCE; -} +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + +/* TRIBES 2 */ +#define TRIBES2_QUERY_GAME_TYPES 2 +#define TRIBES2_QUERY_MASTER 6 +#define TRIBES2_QUERY_PING 14 +#define TRIBES2_QUERY_INFO 18 +#define TRIBES2_RESPONSE_GAME_TYPES 4 +#define TRIBES2_RESPONSE_MASTER 8 +#define TRIBES2_RESPONSE_PING 16 +#define TRIBES2_RESPONSE_INFO 20 + +#define TRIBES2_NO_COMPRESS 2 +#define TRIBES2_DEFAULT_PACKET_INDEX 255 +#define TRIBES2_STATUS_DEDICATED (1<<0) +#define TRIBES2_STATUS_PASSWORD (1<<1) +#define TRIBES2_STATUS_LINUX (1<<2) +#define TRIBES2_STATUS_TOURNAMENT (1<<3) +#define TRIBES2_STATUS_NOALIAS (1<<4) +#define TRIBES2_STATUS_TEAMDAMAGE (1<<5) +#define TRIBES2_STATUS_TOURNAMENT_VER3 (1<<6) +#define TRIBES2_STATUS_NOALIAS_VER3 (1<<7) +char tribes2_game_types_request[] = { TRIBES2_QUERY_GAME_TYPES, 0, 1,2,3,4 }; +char tribes2_ping[] = { TRIBES2_QUERY_PING, TRIBES2_NO_COMPRESS, 1,2,3,4 }; +char tribes2_info[] = { TRIBES2_QUERY_INFO, TRIBES2_NO_COMPRESS, 1,2,3,4 }; +unsigned char tribes2_masterquery[] = { + TRIBES2_QUERY_MASTER, 128, /* <= build 22228, this was 0 */ + 11,12,13,14, + 255, + 3, 'a', 'n', 'y', + 3, 'a', 'n', 'y', + 0, 255, /* min/max players */ + 0xff, 0xff, 0xff, 0xff, /* region mask */ + 0, 0, 0, 0, /* build version */ + 0, /* status */ + 255, /* max bots */ + 0, 0, /* min cpu */ + 0 /* # buddies */ + }; +#define TRIBES2_ID_OFFSET 2 + + + + +{ + /* TRIBES 2 */ + TRIBES2_SERVER, /* id */ + "T2S", /* type_prefix */ + "t2s", /* type_string */ + "-t2s", /* type_option */ + "Tribes 2", /* game_name */ + 0, /* master */ + TRIBES2_DEFAULT_PORT, /* default_port */ + 0, /* port_offset */ + 0, /* flags */ + "game", /* game_rule */ + "TRIBES2", /* template_var */ + (char*) &tribes2_ping, /* status_packet */ + sizeof( tribes2_ping), /* status_len */ + (char*) &tribes2_info, /* player_packet */ + sizeof( tribes2_info), /* player_len */ + (char*) NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_tribes2_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_tribes2_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_tribes2_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_tribes2_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_tribes2_packet, /* packet_func */ +}, + + + + + +query_status_t send_tribes2_request_packet(struct qserver *server) +{ + int rc; + + if (server->flags &FLAG_BROADCAST && server->server_name == NULL) + { + rc = send_broadcast(server, server->type->status_packet, server->type->status_len); + } + else if (server->server_name == NULL) + { + rc = send(server->fd, server->type->status_packet, server->type->status_len, 0); + } + else + { + rc = send(server->fd, server->type->player_packet, server->type->player_len, 0); + } + + if (rc == SOCKET_ERROR) + { + return send_error( server, rc ); + } + + register_send(server); + + return rc; +} + + + + +void get_tribes2_player_type(struct player *player) +{ + char *name = player->name; + for (; *name; name++) + { + switch (*name) + { + case 0x8: + player->type_flag = PLAYER_TYPE_NORMAL; + continue; + case 0xc: + player->type_flag = PLAYER_TYPE_ALIAS; + continue; + case 0xe: + player->type_flag = PLAYER_TYPE_BOT; + continue; + case 0xb: + break; + default: + continue; + } + name++; + if (isprint(*name)) + { + char *n = name; + for (; isprint(*n); n++) + ; + player->tribe_tag = strndup(name, n - name); + name = n; + } + if (! *name) + { + break; + } + } +} + +query_status_t deal_with_tribes2_packet(struct qserver *server, char *pkt, int pktlen) +{ + char str[256], *pktstart = pkt, *term, *start; + unsigned int minimum_net_protocol, build_version, i, t, len, s, status; + unsigned int net_protocol; + unsigned short cpu_speed; + int n_teams = 0, n_players; + struct player **teams = NULL, *player; + struct player **last_player = &server->players; + int query_version; + + debug( 2, "deal_with_tribes2_packet %p, %d", server, pktlen ); + + pkt[pktlen] = '\0'; + + if (server->server_name == NULL) + { + server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); + } + /* + else + gettimeofday( &server->packet_time1, NULL); + */ + + if (pkt[0] == TRIBES2_RESPONSE_PING) + { + if (pkt[6] < 4 || pkt[6] > 12 || strncmp(pkt + 7, "VER", 3) != 0) + { + return PKT_ERROR; + } + + strncpy(str, pkt + 10, pkt[6] - 3); + str[pkt[6] - 3] = '\0'; + query_version = atoi(str); + add_nrule(server, "queryversion", pkt + 7, pkt[6]); + pkt += 7+pkt[6]; + + server->protocol_version = query_version; + if (query_version != 3 && query_version != 5) + { + server->server_name = strdup("Unknown query version"); + return PKT_ERROR; + } + + if (query_version == 5) + { + net_protocol = swap_long_from_little(pkt); + sprintf(str, "%u", net_protocol); + add_rule(server, "net_protocol", str, NO_FLAGS); + pkt += 4; + } + minimum_net_protocol = swap_long_from_little(pkt); + sprintf(str, "%u", minimum_net_protocol); + add_rule(server, "minimum_net_protocol", str, NO_FLAGS); + pkt += 4; + build_version = swap_long_from_little(pkt); + sprintf(str, "%u", build_version); + add_rule(server, "build_version", str, NO_FLAGS); + pkt += 4; + + server->server_name = strndup(pkt + 1, *(unsigned char*)(pkt)); + + /* Always send the player request because the ping packet + * contains very little information */ + send_player_request_packet(server); + return 0; + } + else if (pkt[0] != TRIBES2_RESPONSE_INFO) + { + return PKT_ERROR; + } + + pkt += 6; + for (i = 0; i < *(unsigned char*)pkt; i++) + if (!isprint(pkt[i + 1])) + { + return PKT_ERROR; + } + add_nrule(server, server->type->game_rule, pkt + 1, *(unsigned char*)pkt); + server->game = strndup(pkt + 1, *(unsigned char*)pkt); + pkt += *pkt + 1; + add_nrule(server, "mission", pkt + 1, *(unsigned char*)pkt); + pkt += *pkt + 1; + server->map_name = strndup(pkt + 1, *(unsigned char*)pkt); + pkt += *pkt + 1; + + status = *(unsigned char*)pkt; + sprintf(str, "%u", status); + add_rule(server, "status", str, NO_FLAGS); + if (status &TRIBES2_STATUS_DEDICATED) + { + add_rule(server, "dedicated", "1", NO_FLAGS); + } + if (status &TRIBES2_STATUS_PASSWORD) + { + add_rule(server, "password", "1", NO_FLAGS); + } + if (status &TRIBES2_STATUS_LINUX) + { + add_rule(server, "linux", "1", NO_FLAGS); + } + if (status &TRIBES2_STATUS_TEAMDAMAGE) + { + add_rule(server, "teamdamage", "1", NO_FLAGS); + } + if (server->protocol_version == 3) + { + if (status &TRIBES2_STATUS_TOURNAMENT_VER3) + { + add_rule(server, "tournament", "1", NO_FLAGS); + } + if (status &TRIBES2_STATUS_NOALIAS_VER3) + { + add_rule(server, "no_aliases", "1", NO_FLAGS); + } + } + else + { + if (status &TRIBES2_STATUS_TOURNAMENT) + { + add_rule(server, "tournament", "1", NO_FLAGS); + } + if (status &TRIBES2_STATUS_NOALIAS) + { + add_rule(server, "no_aliases", "1", NO_FLAGS); + } + } + pkt++; + server->num_players = *(unsigned char*)pkt; + pkt++; + server->max_players = *(unsigned char*)pkt; + pkt++; + sprintf(str, "%u", *(unsigned char*)pkt); + add_rule(server, "bot_count", str, NO_FLAGS); + pkt++; + cpu_speed = swap_short_from_little(pkt); + sprintf(str, "%hu", cpu_speed); + add_rule(server, "cpu_speed", str, NO_FLAGS); + pkt += 2; + + if (strcmp(server->server_name, "VER3") == 0) + { + free(server->server_name); + server->server_name = strndup(pkt + 1, *(unsigned char*)pkt); + } + else + { + add_nrule(server, "info", pkt + 1, *(unsigned char*)pkt); + } + + pkt += *(unsigned char*)pkt + 1; + len = swap_short_from_little(pkt); + pkt += 2; + start = pkt; + if (len + (pkt - pktstart) > pktlen) + { + len -= (len + (pkt - pktstart)) - pktlen; + } + + if (len == 0 || pkt - pktstart >= pktlen) + { + goto info_done; + } + + term = strchr(pkt, 0xa); + if (!term) + { + goto info_done; + } + *term = '\0'; + n_teams = atoi(pkt); + sprintf(str, "%d", n_teams); + add_rule(server, "numteams", str, NO_FLAGS); + pkt = term + 1; + + if (pkt - pktstart >= pktlen) + { + goto info_done; + } + + teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams); + for (t = 0; t < n_teams; t++) + { + teams[t] = (struct player*)calloc(1, sizeof(struct player)); + teams[t]->number = TRIBES_TEAM; + teams[t]->team = t; + /* team name */ + term = strchr(pkt, 0x9); + if (!term) + { + n_teams = t; + goto info_done; + } teams[t]->name = strndup(pkt, term - pkt); + pkt = term + 1; + term = strchr(pkt, 0xa); + if (!term) + { + n_teams = t; + goto info_done; + } + *term = '\0'; + teams[t]->frags = atoi(pkt); + pkt = term + 1; + if (pkt - pktstart >= pktlen) + { + goto info_done; + } + } + + term = strchr(pkt, 0xa); + if (!term || term - start >= len) + { + goto info_done; + } + *term = '\0'; + n_players = atoi(pkt); + pkt = term + 1; + + for (i = 0; i < n_players && pkt - start < len; i++) + { + pkt++; /* skip first byte (0x10) */ + if (pkt - start >= len) + { + break; + } + player = (struct player*)calloc(1, sizeof(struct player)); + term = strchr(pkt, 0x11); + if (!term || term - start >= len) + { + free(player); + break; + } player->name = strndup(pkt, term - pkt); + get_tribes2_player_type(player); + pkt = term + 1; + pkt++; /* skip 0x9 */ + if (pkt - start >= len) + { + break; + } + term = strchr(pkt, 0x9); + if (!term || term - start >= len) + { + free(player->name); + free(player); + break; + } + for (t = 0; t < n_teams; t++) + { + if (term - pkt == strlen(teams[t]->name) && strncmp(pkt, teams[t]->name, term - pkt) == 0) + { + break; + } + } + if (t == n_teams) + { + player->team = - 1; + player->team_name = "Unassigned"; + } + else + { + player->team = t; + player->team_name = teams[t]->name; + } + player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; + pkt = term + 1; + for (s = 0; *pkt != 0xa && pkt - start < len; pkt++) + { + str[s++] = *pkt; + } + str[s] = '\0'; + player->frags = atoi(str); + if (*pkt == 0xa) + { + pkt++; + } + + *last_player = player; + last_player = &player->next; + } + +info_done: + for (t = n_teams; t;) + { + t--; + teams[t]->next = server->players; + server->players = teams[t]; + } + if (teams) + { + free(teams); + } + + return DONE_FORCE; +} diff --git a/reference/vcmp/lgsl.txt b/reference/vcmp/lgsl.txt index 22a3cb2..a5f5b7e 100644 --- a/reference/vcmp/lgsl.txt +++ b/reference/vcmp/lgsl.txt @@ -1,90 +1,90 @@ - - /*----------------------------------------------------------------------------------------------------------\ - | | - | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | - | | - | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | - | | - \-----------------------------------------------------------------------------------------------------------*/ - - function lgsl_query_12(&$server, &$lgsl_need, &$lgsl_fp) - { -//---------------------------------------------------------+ -// REFERENCE: -// VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE - - if ($server['b']['type'] == "samp") { $challenge_packet = "SAMP\x21\x21\x21\x21\x00\x00"; } - elseif ($server['b']['type'] == "vcmp") { $challenge_packet = "VCMP\x21\x21\x21\x21\x00\x00"; $lgsl_need['e'] = FALSE; $lgsl_need['p'] = FALSE; } - - if ($lgsl_need['s']) { $challenge_packet .= "i"; } - elseif ($lgsl_need['e']) { $challenge_packet .= "r"; } - elseif ($lgsl_need['p']) { $challenge_packet .= "d"; } - - fwrite($lgsl_fp, $challenge_packet); - - $buffer = fread($lgsl_fp, 4096); - - if (!$buffer) { return FALSE; } - -//---------------------------------------------------------+ - - $buffer = substr($buffer, 10); // REMOVE HEADER - - $response_type = lgsl_cut_byte($buffer, 1); - -//---------------------------------------------------------+ - - if ($response_type == "i") - { - $lgsl_need['s'] = FALSE; - - $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); - $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - $server['s']['name'] = lgsl_cut_pascal($buffer, 4); - $server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4); - $server['s']['map'] = lgsl_cut_pascal($buffer, 4); - } - -//---------------------------------------------------------+ - - elseif ($response_type == "r") - { - $lgsl_need['e'] = FALSE; - - $item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - - for ($i=0; $i<$item_total; $i++) - { - if (!$buffer) { return FALSE; } - - $data_key = strtolower(lgsl_cut_pascal($buffer)); - $data_value = lgsl_cut_pascal($buffer); - - $server['e'][$data_key] = $data_value; - } - } - -//---------------------------------------------------------+ - - elseif ($response_type == "d") - { - $lgsl_need['p'] = FALSE; - - $player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); - - for ($i=0; $i<$player_total; $i++) - { - if (!$buffer) { return FALSE; } - - $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); - $server['p'][$i]['name'] = lgsl_cut_pascal($buffer); - $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); - $server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); - } - } - -//---------------------------------------------------------+ - - return TRUE; - } + + /*----------------------------------------------------------------------------------------------------------\ + | | + | [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | + | | + | Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) | + | | + \-----------------------------------------------------------------------------------------------------------*/ + + function lgsl_query_12(&$server, &$lgsl_need, &$lgsl_fp) + { +//---------------------------------------------------------+ +// REFERENCE: +// VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE + + if ($server['b']['type'] == "samp") { $challenge_packet = "SAMP\x21\x21\x21\x21\x00\x00"; } + elseif ($server['b']['type'] == "vcmp") { $challenge_packet = "VCMP\x21\x21\x21\x21\x00\x00"; $lgsl_need['e'] = FALSE; $lgsl_need['p'] = FALSE; } + + if ($lgsl_need['s']) { $challenge_packet .= "i"; } + elseif ($lgsl_need['e']) { $challenge_packet .= "r"; } + elseif ($lgsl_need['p']) { $challenge_packet .= "d"; } + + fwrite($lgsl_fp, $challenge_packet); + + $buffer = fread($lgsl_fp, 4096); + + if (!$buffer) { return FALSE; } + +//---------------------------------------------------------+ + + $buffer = substr($buffer, 10); // REMOVE HEADER + + $response_type = lgsl_cut_byte($buffer, 1); + +//---------------------------------------------------------+ + + if ($response_type == "i") + { + $lgsl_need['s'] = FALSE; + + $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); + $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + $server['s']['name'] = lgsl_cut_pascal($buffer, 4); + $server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4); + $server['s']['map'] = lgsl_cut_pascal($buffer, 4); + } + +//---------------------------------------------------------+ + + elseif ($response_type == "r") + { + $lgsl_need['e'] = FALSE; + + $item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + + for ($i=0; $i<$item_total; $i++) + { + if (!$buffer) { return FALSE; } + + $data_key = strtolower(lgsl_cut_pascal($buffer)); + $data_value = lgsl_cut_pascal($buffer); + + $server['e'][$data_key] = $data_value; + } + } + +//---------------------------------------------------------+ + + elseif ($response_type == "d") + { + $lgsl_need['p'] = FALSE; + + $player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); + + for ($i=0; $i<$player_total; $i++) + { + if (!$buffer) { return FALSE; } + + $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); + $server['p'][$i]['name'] = lgsl_cut_pascal($buffer); + $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); + $server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); + } + } + +//---------------------------------------------------------+ + + return TRUE; + } diff --git a/reference/worldinconflict/qstat.txt b/reference/worldinconflict/qstat.txt index 739874c..3cf05f2 100644 --- a/reference/worldinconflict/qstat.txt +++ b/reference/worldinconflict/qstat.txt @@ -1,241 +1,241 @@ -LICENSE: The Artistic License 2.0 - -/* - * qstat.h - * by Steve Jankowski - * steve@qstat.org - * http://www.qstat.org - * - * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski - */ - - - -{ - /* World in Confict PROTOCOL */ - WIC_PROTOCOL_SERVER, /* id */ - "WICS", /* type_prefix */ - "wics", /* type_string */ - "-wics", /* type_option */ - "World in Conflict", /* game_name */ - 0, /* master */ - 0, /* default_port */ - 0, /* port_offset */ - TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */ - "N/A", /* game_rule */ - "WICPROTOCOL", /* template_var */ - NULL, /* status_packet */ - 0, /* status_len */ - NULL, /* player_packet */ - 0, /* player_len */ - NULL, /* rule_packet */ - 0, /* rule_len */ - NULL, /* master_packet */ - 0, /* master_len */ - NULL, /* master_protocol */ - NULL, /* master_query */ - display_wic_player_info, /* display_player_func */ - display_server_rules, /* display_rule_func */ - raw_display_wic_player_info, /* display_raw_player_func */ - raw_display_server_rules, /* display_raw_rule_func */ - xml_display_wic_player_info, /* display_xml_player_func */ - xml_display_server_rules, /* display_xml_rule_func */ - send_wic_request_packet, /* status_query_func */ - NULL, /* rule_query_func */ - NULL, /* player_query_func */ - deal_with_wic_packet, /* packet_func */ -}, - - -/* - * qstat 2.8 - * by Steve Jankowski - * - * World in Conflict Protocol - * Copyright 2007 Steven Hartland - * - * Licensed under the Artistic License, see LICENSE.txt for license terms - * - */ - -#include -#ifndef _WIN32 -#include -#endif -#include -#include -#include - -#include "debug.h" -#include "qstat.h" -#include "packet_manip.h" - - -query_status_t send_wic_request_packet( struct qserver *server ) -{ - char buf[256]; - - int serverport = get_param_i_value( server, "port", 0 ); - char *password = get_param_value( server, "password", "N/A" ); - change_server_port( server, serverport, 1 ); - - if ( get_player_info ) - { - server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY; - sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/listplayers\x0d\x0a/exit\x0d\x0a", password ); - server->saved_data.pkt_index = 2; - } - else - { - server->flags |= TF_STATUS_QUERY; - sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/exit\x0d\x0a", password ); - server->saved_data.pkt_index = 1; - } - - return send_packet( server, buf, strlen( buf ) ); -} - - -query_status_t deal_with_wic_packet( struct qserver *server, char *rawpkt, int pktlen ) -{ - char *s, *end, *team = NULL; - int mode = server->n_servers, slot, score; - char name[256], role[256]; - - debug( 2, "processing n_requests %d, retry1 %d, n_retries %d, delta %d", server->n_requests, server->retry1, n_retries, time_delta( &packet_recv_time, &server->packet_time1 ) ); - - server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 ); - server->n_requests++; - - if ( 0 == pktlen ) - { - // Invalid password - return REQ_ERROR; - } - - gettimeofday( &server->packet_time1, NULL); - - rawpkt[pktlen]= '\0'; - end = &rawpkt[pktlen]; - - s = rawpkt; - - while ( NULL != s ) - { - int len = strlen( s ); - *(s + len - 2) = '\0'; // strip off \x0D\x0A - debug( 2, "Line[%d]: %s", mode, s ); - - if ( 0 == mode ) - { - // Settings - // TODO: make parse safe - if ( 0 == strncmp( s, "Settings: ", 9 ) ) - { - // Server Rule - char *key = s + 10; - char *value = strchr( key, ' ' ); - *value = '\0'; - value++; - debug( 2, "key: '%s' = '%s'", key, value ); - if ( 0 == strcmp( "myGameName", key ) ) - { - server->server_name = strdup( value ); - } - else if ( 0 == strcmp( "myMapFilename", key ) ) - { - server->map_name = strdup( value ); - } - else if ( 0 == strcmp( "myMaxPlayers", key ) ) - { - server->max_players = atoi( value ); - } - else if ( 0 == strcmp( "myCurrentNumberOfPlayers", key ) ) - { - server->num_players = atoi( value); - } - else - { - add_rule( server, key, value, NO_FLAGS ); - } - } - else if ( 0 == strcmp( "Listing players", s ) ) - { - // end of rules request - server->saved_data.pkt_index--; - mode++; - } - else if ( 0 == strcmp( "Exit confirmed.", s ) ) - { - server->n_servers = mode; - return DONE_FORCE; - } - } - else if ( 1 == mode ) - { - // Player info - if ( 0 == strncmp( s, "Team: ", 6 ) ) - { - team = s + 6; - debug( 2, "Team: %s", team ); - } - else if ( 4 == sscanf( s, "Slot: %d Role: %s Score: %d Name: %255[^\x0d\x0a]", &slot, role, &score, name ) ) - { - // Player info - struct player *player = add_player( server, server->n_player_info ); - if ( NULL != player ) - { - player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; - player->name = strdup( name ); - player->score = score; - player->team_name = team; - player->tribe_tag = strdup( role ); - // Indicate if its a bot - player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0; - } - debug( 2, "player %d, role %s, score %d, name %s", slot, role, score, name ); - } - else if ( 3 == sscanf( s, "Slot: %d Role: Score: %d Name: %255[^\x0d\x0a]", &slot, &score, name ) ) - { - // Player info - struct player *player = add_player( server, server->n_player_info ); - if ( NULL != player ) - { - player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; - player->name = strdup( name ); - player->score = score; - player->team_name = team; - // Indicate if its a bot - player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0; - } - debug( 2, "player %d, score %d, name %s", slot, score, name ); - } - else if ( 0 == strcmp( "Exit confirmed.", s ) ) - { - server->n_servers = mode; - return DONE_FORCE; - } - } - - s += len; - if ( s + 1 < end ) - { - s++; // next line - } - else - { - s = NULL; - } - } - - server->n_servers = mode; - - if ( 0 == server->saved_data.pkt_index ) - { - server->map_name = strdup( "N/A" ); - return DONE_FORCE; - } - - return INPROGRESS; -} - +LICENSE: The Artistic License 2.0 + +/* + * qstat.h + * by Steve Jankowski + * steve@qstat.org + * http://www.qstat.org + * + * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski + */ + + + +{ + /* World in Confict PROTOCOL */ + WIC_PROTOCOL_SERVER, /* id */ + "WICS", /* type_prefix */ + "wics", /* type_string */ + "-wics", /* type_option */ + "World in Conflict", /* game_name */ + 0, /* master */ + 0, /* default_port */ + 0, /* port_offset */ + TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */ + "N/A", /* game_rule */ + "WICPROTOCOL", /* template_var */ + NULL, /* status_packet */ + 0, /* status_len */ + NULL, /* player_packet */ + 0, /* player_len */ + NULL, /* rule_packet */ + 0, /* rule_len */ + NULL, /* master_packet */ + 0, /* master_len */ + NULL, /* master_protocol */ + NULL, /* master_query */ + display_wic_player_info, /* display_player_func */ + display_server_rules, /* display_rule_func */ + raw_display_wic_player_info, /* display_raw_player_func */ + raw_display_server_rules, /* display_raw_rule_func */ + xml_display_wic_player_info, /* display_xml_player_func */ + xml_display_server_rules, /* display_xml_rule_func */ + send_wic_request_packet, /* status_query_func */ + NULL, /* rule_query_func */ + NULL, /* player_query_func */ + deal_with_wic_packet, /* packet_func */ +}, + + +/* + * qstat 2.8 + * by Steve Jankowski + * + * World in Conflict Protocol + * Copyright 2007 Steven Hartland + * + * Licensed under the Artistic License, see LICENSE.txt for license terms + * + */ + +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +#include "debug.h" +#include "qstat.h" +#include "packet_manip.h" + + +query_status_t send_wic_request_packet( struct qserver *server ) +{ + char buf[256]; + + int serverport = get_param_i_value( server, "port", 0 ); + char *password = get_param_value( server, "password", "N/A" ); + change_server_port( server, serverport, 1 ); + + if ( get_player_info ) + { + server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY; + sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/listplayers\x0d\x0a/exit\x0d\x0a", password ); + server->saved_data.pkt_index = 2; + } + else + { + server->flags |= TF_STATUS_QUERY; + sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/exit\x0d\x0a", password ); + server->saved_data.pkt_index = 1; + } + + return send_packet( server, buf, strlen( buf ) ); +} + + +query_status_t deal_with_wic_packet( struct qserver *server, char *rawpkt, int pktlen ) +{ + char *s, *end, *team = NULL; + int mode = server->n_servers, slot, score; + char name[256], role[256]; + + debug( 2, "processing n_requests %d, retry1 %d, n_retries %d, delta %d", server->n_requests, server->retry1, n_retries, time_delta( &packet_recv_time, &server->packet_time1 ) ); + + server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 ); + server->n_requests++; + + if ( 0 == pktlen ) + { + // Invalid password + return REQ_ERROR; + } + + gettimeofday( &server->packet_time1, NULL); + + rawpkt[pktlen]= '\0'; + end = &rawpkt[pktlen]; + + s = rawpkt; + + while ( NULL != s ) + { + int len = strlen( s ); + *(s + len - 2) = '\0'; // strip off \x0D\x0A + debug( 2, "Line[%d]: %s", mode, s ); + + if ( 0 == mode ) + { + // Settings + // TODO: make parse safe + if ( 0 == strncmp( s, "Settings: ", 9 ) ) + { + // Server Rule + char *key = s + 10; + char *value = strchr( key, ' ' ); + *value = '\0'; + value++; + debug( 2, "key: '%s' = '%s'", key, value ); + if ( 0 == strcmp( "myGameName", key ) ) + { + server->server_name = strdup( value ); + } + else if ( 0 == strcmp( "myMapFilename", key ) ) + { + server->map_name = strdup( value ); + } + else if ( 0 == strcmp( "myMaxPlayers", key ) ) + { + server->max_players = atoi( value ); + } + else if ( 0 == strcmp( "myCurrentNumberOfPlayers", key ) ) + { + server->num_players = atoi( value); + } + else + { + add_rule( server, key, value, NO_FLAGS ); + } + } + else if ( 0 == strcmp( "Listing players", s ) ) + { + // end of rules request + server->saved_data.pkt_index--; + mode++; + } + else if ( 0 == strcmp( "Exit confirmed.", s ) ) + { + server->n_servers = mode; + return DONE_FORCE; + } + } + else if ( 1 == mode ) + { + // Player info + if ( 0 == strncmp( s, "Team: ", 6 ) ) + { + team = s + 6; + debug( 2, "Team: %s", team ); + } + else if ( 4 == sscanf( s, "Slot: %d Role: %s Score: %d Name: %255[^\x0d\x0a]", &slot, role, &score, name ) ) + { + // Player info + struct player *player = add_player( server, server->n_player_info ); + if ( NULL != player ) + { + player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; + player->name = strdup( name ); + player->score = score; + player->team_name = team; + player->tribe_tag = strdup( role ); + // Indicate if its a bot + player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0; + } + debug( 2, "player %d, role %s, score %d, name %s", slot, role, score, name ); + } + else if ( 3 == sscanf( s, "Slot: %d Role: Score: %d Name: %255[^\x0d\x0a]", &slot, &score, name ) ) + { + // Player info + struct player *player = add_player( server, server->n_player_info ); + if ( NULL != player ) + { + player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; + player->name = strdup( name ); + player->score = score; + player->team_name = team; + // Indicate if its a bot + player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0; + } + debug( 2, "player %d, score %d, name %s", slot, score, name ); + } + else if ( 0 == strcmp( "Exit confirmed.", s ) ) + { + server->n_servers = mode; + return DONE_FORCE; + } + } + + s += len; + if ( s + 1 < end ) + { + s++; // next line + } + else + { + s = NULL; + } + } + + server->n_servers = mode; + + if ( 0 == server->saved_data.pkt_index ) + { + server->map_name = strdup( "N/A" ); + return DONE_FORCE; + } + + return INPROGRESS; +} +